Here is R Notebook Guideline

This is an R Markdown Notebook. When you execute code within the notebook, the results appear beneath the code.

Try executing this chunk by clicking the Run button within the chunk or by placing your cursor inside it and pressing Ctrl+Shift+Enter.

plot(cars)
# Load ggplot2 package
if(!suppressWarnings(require(ggplot2)))
{
    install.packages('ggplot2')
    require(ggplot2)
}

Add a new chunk by clicking the Insert Chunk button on the toolbar or by pressing Ctrl+Alt+I.

When you save the notebook, an HTML file containing the code and output will be saved alongside it (click the Preview button or press Ctrl+Shift+K to preview the HTML file).

201706 ggplot2 Learning Road-Stone_Hou.md

Grammar of Graphics(gg) ggplot2的图形语法

Understanding the root of ggplot2

A statistical graphic is a mapping from data to aesthetic attributes (colour,shape, size) of geometric objects (points, lines, bars). By Handley(author of dplyr, plyr,stringr,ggplot2,rvest packages, and of cause, tidy verse(ggplot2 for ))

tidy verse package for data science

ggplot2, for data visualisation. dplyr, for data manipulation. tidyr, for data tidying. readr, for data import. purrr, for functional programming. tibble, for tibbles, a modern re-imagining of data frames

Before vs After

x <- rnorm(100,14,5) 
y <- x + rnorm(100,0,1) 
p <- ggplot(data= NULL, aes(x = x, y = y)) +  #开始绘图
  geom_point(color = "darkred") +  #添加点
  annotate("text",x =13 , y = 20,parse = T,
           label = "x[1] == x[2]") #添加注释
p

ggplot图的元素可以主要可以概括如下: 最大的是plot(指整张图,包括background和title),

其次是axis(包括stick,text,title和stick)、legend(包括backgroud、text、title)、facet这是第二层次,其中facet可以分为外部strip部分(包括backgroud和text)和内部panel部分(包括backgroud、boder和网格线grid,其中粗的叫grid.major,细的叫grid.minor)。

ggplot2风格的绘图的第一步就是初始化,说白了就是载入数据空间、选择数据以及选择默认aes。

p <- ggplot(data = , aes(x = , y = ))

data就是载入你要画的数据所在的数据框,指定为你的绘图环境,载入之后,就可以免去写大量的$来提取data.frame之中的向量。

当然,如果你的数据都是向量,也可不指定,但是要在申明中标注data = NULL,不然就会得到不必要的报错。

Key Point:

  1. ggplot2的核心理念是将绘图与数据分离,数据相关的绘图与数据无关的绘图分离
  1. ggplot2是按图层作图
  1. ggplot2保有命令式作图的调整函数,使其更具灵活性
  1. ggplot2将常见的统计变换融入到了绘图中。

how_to_use_gggplot2

尽管qplot作为ggplot2的快速作图(quick plot)函数, 能够极大的简化作图步骤, 容易入门和上手, 但是qplot却不是泛型函数, 而ggplot()作为泛型函数, 能对任意类型的R对象进行可视化操作, 是ggplot2的精髓所在, 因而在本文中主要的绘图都是通过ggplot()来完成的。有关于qplot的介绍可以细看Hadley的官方介绍。

在Hadley的ggplot2官方文档中, Hadely这样对Wilkinson的图形语法进行了描述:

“一张统计图形就是从数据到几何对象(geometric object, 缩写为geom, 包括点、线、条形等)的图形属性(aesthetic attributes, 缩写为aes, 包括颜色、形状、大小等)的一个映射。此外, 图形中还可能包含数据的统计变换(statistical transformation, 缩写为stats), 最后绘制在某个特定的坐标系(coordinate system, 缩写为coord)中, 而分面(facet, 指将绘图窗口划分为若干个子窗口)则可以用来生成数据中不同子集的图形。”

因此在ggplot2中, 图形语法中至少包括了如下7个图形部件以及两个效率组件(主题,存储+输出):

1. 数据(data)

数据(data)包含了变量variable与数值

  1. 在ggplot2中, 所接受的数据集必须为数据框(data.frame)格式, 如内置的mtcars数据集:
head(mtcars)
#                    mpg cyl disp  hp drat    wt  qsec vs am gear carb
# Mazda RX4         21.0   6  160 110 3.90 2.620 16.46  0  1    4    4
# Mazda RX4 Wag     21.0   6  160 110 3.90 2.875 17.02  0  1    4    4
# Datsun 710        22.8   4  108  93 3.85 2.320 18.61  1  1    4    1
# Hornet 4 Drive    21.4   6  258 110 3.08 3.215 19.44  1  0    3    1
# Hornet Sportabout 18.7   8  360 175 3.15 3.440 17.02  0  0    3    2
# Valiant           18.1   6  225 105 2.76 3.460 20.22  1  0    3    1
  1. 这种格式带来的好处是数据易于存储, 也能在保留原有的绘图参数下, 用%+%方便地变更已有数据集。

为通过”+”以图层的方式加入点的几何对象

p <- ggplot(mtcars, aes(mpg, wt, colour = cyl)) +
  geom_point() #geom_point()为通过”+”以图层的方式加入点的几何对象
p

# Data transform
mtcars.c <- transform(mtcars, mpg = mpg^2)

#用mtcars.c替换mtcars
p %+% mtcars.c 
  1. 而ggplot2进行数据分组时必须根据行, 而不能根据列, 例如在mtcars的数据集中, 可以把汽车按汽缸数进行分组, 但不能按汽车的档位数和汽缸数这两个变量分为两组。这要求把“宽”数据转化为“长”数据。所谓的长数据是变量不再是放在各个列上, 而是排成一列, 每一个变量都分别占其中的几行, 这样就能方便的对每个变量进行分组。reshape2中melt()和cast()能够灵活的融合(melt)和重铸(cast)在数据框中的数据。

# Load reshape2 package
if(!suppressWarnings(require(reshape2)))
{
    install.packages('reshape2')
    require(reshape2)
}

# 融合档位数gear和汽缸数cyl这两个变量到一列,对应的值到另一列
# mpg cyl disp  hp drat    wt  qsec vs am gear carb
mtcars.m <- melt(mtcars, id = c("mpg", "disp", "hp", "drat", "wt", "qsec", "vs", "carb")) 

#head(mtcars.m,20)
head(mtcars)
head(mtcars.m)

2. 映射(mapping)

(1) 映射的概念

aes()函数是ggplot2中的映射函数, 所谓的映射即为数据集中的数据值关联到相应的图形属性(aesthetic)过程中一种对应关系, 图形属性(aesthetic)包括:横纵坐标、点的大小、颜色、形状、填充色等。attributes,简称aes?? 例如:

p1 <- ggplot(data = mtcars)
summary(p1)
# data: mpg, cyl, disp, hp, drat, wt, qsec, vs,
#   am, gear, carb [32x11]
# faceting: <ggproto object: Class FacetNull, Facet>
#     compute_layout: function
#     draw_back: function
#     draw_front: function
#     draw_labels: function
#     draw_panels: function
#     finish_data: function
#     init_scales: function
#     map: function
#     map_data: function
#     params: list
#     render_back: function
#     render_front: function
#     render_panels: function
#     setup_data: function
#     setup_params: function
#     shrink: TRUE
#     train: function
#     train_positions: function
#     train_scales: function
#     vars: function
#     super:  <ggproto object: Class FacetNull, Facet>

p2 <- ggplot(data = mtcars, mapping = aes(x = wt, y = hp, color = gear))
summary(p2)
# data: mpg, cyl, disp, hp, drat, wt, qsec, vs,
#   am, gear, carb [32x11]
# mapping:  x = wt, y = hp, colour = gear
# faceting: <ggproto object: Class FacetNull, Facet>
#     compute_layout: function
#     draw_back: function
#     draw_front: function
#     draw_labels: function
#     draw_panels: function
#     finish_data: function
#     init_scales: function
#     map: function
#     map_data: function
#     params: list
#     render_back: function
#     render_front: function
#     render_panels: function
#     setup_data: function
#     setup_params: function
#     shrink: TRUE
#     train: function
#     train_positions: function
#     train_scales: function
#     vars: function
#     super:  <ggproto object: Class FacetNull, Facet>

可以发现, 在p2中, 通过aes()指定了横纵坐标分别为wt和hp, 颜色为gear这三种图形属性,在mapping里边了。 在ggplot2中不同的几何对象对应着不同的图形属性, 有关于几何对象的将在下面的小节讲解。

任何与数据向量顺序相关,需要逐个指定的参数都必须写在aes里。

(2) 设定和映射

映射是将一个变量中离散或连续的数据与一个图形属性中以不同的参数来相互关联, 而设定能够将这个变量中所有的数据统一为一个图形属性。

p <- ggplot(mtcars, aes(wt, mpg))

#设定散点的颜色为蓝色
p + geom_point(color = "blue") 

# 下面的最后一行语句为错误的映射关系, 
# 在aes中, color = “blue”的实际意思是把”blue”当为一个变量, 
# 用这个变量里的数据去关联图形属性中的参数,
# 因为”blue”只含有一个字符变量, 默认情况下为离散变量,
# 按默认的颜色标度标记为桃红色
p + geom_point(aes(color = "blue"))

(3) 分组(group)

分组(group)也是ggplot2种映射关系的一种, 默认情况下ggplot2把所有观测点分为了一组, 如果需要把观测点按额外的离散变量进行分组处理, 必须修改默认的分组设置。

p1 <- ggplot(data = mtcars, mapping = aes(x = wt, y = hp)) + geom_line() 
#默认分组设置, 即group=1
#geom_line为折线图的几何对象
p2 <- ggplot(data = mtcars, mapping = aes(x = wt, y = hp, group = factor(gear))) + geom_line() 
#把wt和hp所对应的观测点按gear(gear以因子化变为离散变量)进行分组

(4) 图层(layer)

这些组件之间是通过“+”, 以图层(layer)的方式来粘合构图的, 所以图层是ggplot2中一个重要的概念。

在上文中通过对数据和映射的讲解中, 我们已经采用过”+”来添加图层, ggplot2中图层的概念和PS中图层的概念很像, 可以这样理解ggplot2中的图层:每个图层可以代表一个图形组件, 例如下面要介绍的几何对象、统计变换等图形组件, 这些组件以图层的方式叠加在一起构成一个绘图的整体, 在每个图层中的图形组件又可以分别设定数据、映射或其他相关参数, 因此组件之间又是具有相对独立性的。ggplot2中图层的设定是十分成功的, 因为这一过程是如此实用、方便而富有逻辑性。

(1) 在几何对象中设定映射

前面我们已在ggplot()中设定了映射了关系, 这种映射关系是默认的, 我们可以在后面的几何对象中沿用已设定的默认映射关系, 也可以随时在几何对象中进行更改。

p <- ggplot(mtcars, aes(x = mpg, y = wt, color = factor(gear)))
#设定默认的映射关系

p + geom_point()
#沿用默认的映射关系来绘制散点图

p + geom_point(aes(shape = factor(carb))) 
#添加图层中的shape的映射关系

p + geom_point(aes(y = carb))
#修改默认的y的映射关系, 注意图中y轴名称仍然以默认的wt表示

p + geom_point(aes(color = NULL))
 #删除默认的color映射关系

尽管上面三个有关例子在实际应用中很少去涉及, 但是很好的说明了图层、数据和映射之间的关系。

(2) 采用多个数据集或向量数据绘图

在很多种绘图场合中, 我们会运用到多个数据集或向量数据来进行图层叠加, 具体的例子如下

#构建不同于mtcars的数据集mtcars.c
mtcars.c <- transform(mtcars, mpg = mpg^2)

ggplot()+
  geom_point(aes(x = hp, y = mpg), data = mtcars, color = "red") + 
  geom_point(aes(x = mtcars$hp, y = mtcars$disp), color = "green")+ 
  #选用向量数据
  geom_point(aes(x = hp, y= mpg), data = mtcars.c, color = "blue") 
  #选用不同的数据集
(3) 加注释

所有注释的实现都是通过annotate函数实现的,其实annotate就是一个最简单的geom_单元,它一次只添加一个位置上的图形(可以通过设置向量来实现同时绘制多个图形,但这个理念和注释的理念有所偏差)。annotate的geom就是指定注释的类型,其属性按照geom的不同而发生变化。

3. 几何对象(geom)

上面指定的图形属性需要呈现在一定的几何对象上才能被我们看到,这些承载图形属性的对象可能是点,可能是线,可能是bar。几何对象执行着图层的实际渲染, 控制着生成的图像类型。

例如用geom_point()将会生成散点图, 而geom_line会生成折线图。

最常见的图有散点图、折线图、曲线图、条形图、直方图,分别对应geom_point(), geom_line(), geom_smooth(), geom_bar(), geom_histogram()。相应的aes中的变量不一定是x和y,对于条形图和直方图就只有一个变量x。

每一个几何对象都有一个默认的统计变换, 并且每一个统计变换都有一个默认的几何对象。正因如此, 这一设定将会使绘图过程变的灵活多变。

在ggplot2的官方索引中, 已对ggplot2中所有的geom和stat组件进行了汇总, 更详细的内容, 可直接点开相应图形组件所对应的链接。下面对几个常用的几何对象和统计变换进行举例描述。

(1) geom_point()散点图

p <- ggplot(mtcars, aes(wt, mpg))
p + geom_point()

#更改颜色-连续变量
p + geom_point(aes(color = qsec))
# p + geom_point(aes(color = factor(qsec)))

#更改颜色-离散变量
p + geom_point(aes(color = factor(gear)))

#更改透明度
p + geom_point(aes(alpha = qsec))

#更改形状
p + geom_point(aes(shape = factor(gear)))

#更改点大小
p + geom_point(aes(size = qsec))


#两种颜色的叠加
p + geom_point(color = "grey50", size = 5) + geom_point(aes(color = qsec), size = 4)

#颜色和形状的叠加
p + geom_point(color = "grey50", size = 5) + geom_point(aes(shape = factor(gear)), size = 3)

p + geom_point(aes(size = qsec, shape = factor(gear), color = factor(gear)))

(2) geom_histogram()

rating <- runif(1000,min = 1, max = 10)
id <- 1:1000
movies <- data.frame(id, rating)

m <- ggplot(movies, aes(rating)) 
#这里使用movies数据集

m + geom_histogram()
m + geom_histogram(binwidth = 0.5)
#调整分箱(bin)数据
m + geom_histogram(binwidth = 1)
m + geom_histogram(binwidth = 2)

geom_histogram()这个几何对象默认使用stat_bin这个统计变换, 而这个统计变换会生成 (1)count:每个组里观测值的数目, (2)density:每个组里观测值的密度和 (3)x:组的中心位置 这三个变量。 生成的这三个变量在ggplot()中的再使用..围起来, 因此可以用来生成如下的图

m + geom_histogram(binwidth = 0.5, aes(fill =..count..))
m + geom_histogram(binwidth = 0.5, aes(y = ..density..)) + geom_density()

柱形图元素位置的调整

元素位置的调整共有5种: (1)dodge:并排方式; (2)fill:堆叠图像元素, 并将高度标准化为1, (3)identity:不做任何调整; (4)jitter:给点增加扰动避免重合, jitter使某每一个点在x轴的方向上产生随机的偏移, 从而减少了图形重叠的问题, 另一种介绍重叠的方式是改变点的透明度, 将在实战中的地图讨论。 (5)stack:堆叠图像元素。

d <- ggplot(diamonds, aes(x = clarity, fill = cut ))
# dodge
d + geom_histogram(position = "dodge", stat = "count")

# fill and standard to 1 stack
d + geom_histogram(position = "fill", stat = "count")

# just stack
d + geom_histogram(position = "stack", stat = "count")

# jitter
ggplot(diamonds) + geom_point(aes(color, price/carat), position = "jitter")

很多情况下, 我们会采用固定的x轴和y轴值来进行作图, 此时需要用stat = “identity” 来申明, 即表示不对数据进行统计变换

A <- c(1, 2, 3, 4, 5, 6, 7, 8)
B <- c(2, 10, 11, 5, 6, 1, 10, 20)
ggplot() + geom_histogram(aes(x = A, y = B), stat = "identity")

(3) geom_smooth()

geom_smooth()用来给数据添加平滑曲线, 所能采用的方法包括了lm, glm, gam, loess, rlm等, 这些方法需要通过加载公式来实现。

m <- ggplot(mtcars, aes(qsec, wt))
# m

# 添加平滑曲线 + 点图
m + stat_smooth() + geom_point()

#取消平滑曲线默认的置信区间 + 点图
m + stat_smooth(se = FALSE) + geom_point()

#更改置信区间和线条颜色
m + stat_smooth(fill = "red", size = 2, alpha = 0.5, color = "green") + geom_point()

#用一元一次(线性回归)方程拟合
m + stat_smooth(method = "lm") + geom_point() 

#使用一元二次方程拟合
m + stat_smooth(method = "lm", formula = y ~ poly(x, 3)) + geom_point() 


# 加载splines和MASS包, 使用自由度为3的自然样条来进行拟合
require(splines)
require(MASS)
m + stat_smooth(method = "lm", formula = y ~ ns(x, 3)) + geom_point()

m <- ggplot(mtcars, aes(y = wt, x = mpg, group = factor(cyl)))

#按cyl这个离散变量进行分组, 分别拟合数据
m + stat_smooth(method = lm, aes(color = factor(cyl), fill = factor(cyl))) + geom_point( aes(color = factor(cyl)))

得益于r包的丰富性, 我们可以采用极大似然或最小二乘法, 对多种现有的函数或自编函数来拟合曲线。例如, 在对剂量-效应曲线绘图的实战中, 采用drc包中的log-logistics四参数方程来拟合剂量-效应曲线。

在这偏不长的博文中, 很难对ggplot2中所有几何对象和统计变换一一详尽, 更详细的内容可以在ggplot2的官方索引找到。但是通过前一小节有关于图层的讲解, 我们已能很容易的通过这些组件整合出一些复杂的图形, 而这些组件的原理也是相同的。

4. 统计变换(stats)

当我们需要展示出某个变量的某种统计特征的时候,需要用到统计变换。比如求均值,求方差等,通常以某种方式对数据信息进行汇总, 例如通过stat_smooth()添加光滑曲线。

每一个几何对象都有一个默认的统计变换, 并且每一个统计变换都有一个默认的几何对象。正因如此, 这一设定将会使绘图过程变的灵活多变。

很多人在解释ggplot2的时候喜欢说,ggplot2绘图有两种函数,一类是geom_,绘图用的;一类是stat_,统计变换用的。这样说不是不对,只是很不恰当,很多人就会问出一些问题,比如,统计变换竟然是做运算用的,为什么可以用来画图?为什么stat_bin和geom_histgram画出来的图是一样,竟然一样,为什么要重复?事实上,任何一个ggplot2图层都包括stat和geom俩部分,或者说两个步骤(其实还包括position)。 而stat_identity则表示不做任何的统计变换。我们来举个例子,还是上面的代码,为了更直观,我在此作了修改:

x <- c(rnorm(100,14,5),rep(20,20)) 
y <- c(rnorm(100,14,5) + rnorm(100,0,1),rep(20,20))
ggplot(data= NULL, aes(x = x, y = y)) +  #开始绘图
  geom_point(color = "darkred")

我们查看码源,就知道geom_point的默认stat是identity,即不做任何统计变换:

geom_point
# function (mapping = NULL, data = NULL, stat = "identity", position = "identity", 
#     na.rm = FALSE, ...) 
# {
#     GeomPoint$new(mapping = mapping, data = data, stat = stat, 
#         position = position, na.rm = na.rm, ...)
# }
# <environment: namespace:ggplot2>

大家可以发现,我在(20,20)这个点的数据事实上是有20个的,但由于没做统计转换(20,20)这个点被画了20次,因此我们理论上看到的点其实是最后一次画的那个点。可能这不够直观,没关系,我们调整一下透明度到10%:

ggplot(data= NULL, aes(x = x, y = y)) +  #开始绘图
  geom_point(color = "darkred",alpha = 0.1)

这样应该就很明显了,由于(20,20)点被画了20次,所以透明度会叠加为20*10% = 200%实际只展现100%。我们现在就使用坐标转换来重新画这个图:

ggplot(data= NULL, aes(x = x, y = y)) +  #开始绘图
  geom_point(color = "darkred",stat = "sum")

好了,解释一下,stat_sum实际的意思就是按照某一点占所有点出现频率然后换算成大小来作图,因此,以上代码就可以得到下面这张图,因为(20,20)这个点出现频率为20/120=16.667%:

好了,我们可以发现了,一个单纯的geom_point里面也是带有stat_的,因此,其实geom_和stat_实际上是一回事。可能你会问了,那照我的说法,以上这幅图用的是geom_point里的一个参数,而不是再用stat_sum,这是一回事吗?bingo!这个问题相当好,的确,按照以上的推理,应该存在一种以stat_sum作为主函数的方法来绘制这幅图,搞不好,里面还有个参数geom,要设置成“point”。我们来实践一下吧:

ggplot(data= NULL, aes(x = x, y = y)) +  #开始绘图
  stat_sum(color = "darkred",geom = "point")

# statistics 其实geom都包含了默认的stat,
# 比如geom_bar默认的就是geom_bar(stat = "count")。
# 所以下面例子里用stat_count( )的结果和geom_bar( )一样。
ggplot(mpg, aes(class)) +
  stat_count()

ggplot(mpg, aes(class)) +
  geom_bar(stat = "count")

还真可以,还长得一模一样。

现在就讲通了,对于有过经验的同学现在应该重新修正这个观点——stat_和geom_是两种绘图方法。

这是错的,其实它们是ggplot2每一个图层绘制都必须有的,是一个图层的一体两面。

在这一步之中,我们也要回到我们在第一步时出现的问题,aes到底是什么?为什么说任何与数据向量顺序相关,需要逐个指定的参数都必须写在aes里?什么时候color、shape、size、fill写外面,什么时候写里面?

aes实际上做的是将aes里的向量的顺序逐个地绘制。譬如以下代码(转自geom_point帮助文档中的实例):

p <- ggplot(mtcars, aes(wt, mpg)) #<---- code 1
# code 1: ggplot首先载入了这个mtcars的集合,然后指定给了mpg作为其x坐标位置,wt为y坐标位置。

p + geom_point(aes(colour = qsec)) #<---- code 2
# code 2: 指定了qsec作为其染色的标准(分组),qsec为numeric变量,因此,应该选择连续型的标尺,而不是分组染色。然后开始绘制,读取mtcars$mpg[1]、mtcars$wt[1],确定位置,然后为其染成mtcars$qsec[1]颜色;再绘制第二点。。。

因此,aes里的美学特征其实就是按照向量顺序指定每个位置的美学特征,大家可以比较tapply函数的写法。

好了,现在问题就来了。我想为所有点的颜色都染成绿色,怎么办?其实很简单,如果不需要指定这么一个染色的顺序,而选择将整个图层染成一种颜色,则只需要将color写在aes外:

p + geom_point(color = "green") 

哦,怪不得写在aes里染出来的颜色不是绿色,但为什么写到里面就不可以了,为了写到里面,然出来的是粉色?

好了,我们再来分析一下把color = “green”写到了aes里,到底发生了什么。

p + geom_point(aes(colour = "green"))

首先,数据的初始化跟上面那个例子是相同的。然后,因为color放到了aes里,于是ggplot开始搜索mtcars里面的向量了,发现没有叫“green”的,然后又找了global,也没有。于是,ggplot就开始把它认作了一个新的向量。

等等,有个问题,我要按照这个向量来分别染色,而事实上,这个向量长度为1,怎么办?ggplot就先把他展开成了factor(rep("green",nrow(mtcars)),levels = unique("green")),bingo!

现在开始染色了。啊第一个数据mtcars$mpg[1]mtcars$wt[1],其颜色变量是“green”,因子水平是1,染成默认调色第一种,哦,就是这个蛋蛋的粉红色;再染第二个,还是“green”,因子水平也是1,染成蛋蛋的粉红色;…

终于完成了,咦?怎么都是蛋蛋的粉红色。通过举了这个染色的例子大家应该都弄懂了,aes到底在干什么了。其他的美学特征其实也是完全一致的。只是需要解释group=1的意思就是说不做分组来进行绘图。

什么?还是搞不清该放aes里面还是外面?那就记着:

想统一整个图层时就放到aes外,想分成不同组调整,并且已经有一个与x、y长度一致的分组变量了,那就放到aes里。

在这一步里,还要要说的就是我们要讲的是ggplot2大致内置了哪些图:

  1. 点(point, text):往往只有x、y指定位置,有shape但没有fill

  2. 线(line,vline,abline,hline,stat_function等):一般是基于函数来处理位置

  3. 有向射线(segment):特征是指定位置有xend和yend,表示射线方向

  4. 面(tile, rect):这类一般有xmax,xmin,ymax,ymin指定位置

  5. 棒(boxplot,bin,bar,histogram):往往是二维或一维变量,具有width属性

  6. 带(ribbon,smooth):透明是特征,是透明的fill

  7. 补:包括rug图,误差棒(errorbar,errorbarh)

然后,就是按照你的需要一步步加图层了(使用“+”)。

5. 标度(scale)

坐标的比例值 标度控制着数据到图形属性的映射, 更重要的一点是标度将我们的数据转化为视觉上可以感知的东西, 如大小、颜色、位置和形状。所以通过标度可以修改坐标轴和图例的参数。

按表1所示, 所有标度构建器(scale constructor)都拥有一套通用的命名方案。它们以scale_开头, 接下来是图形属性的名称(例如, color_、shape_或x_)最后以标度的名称结尾(例如gradient、hue或manual)。从表中可以发现, 标度是区分离散和连续变量的, 因此再对标度进行调整一定要注意区分。

ggplot2中的标度可以粗略的分为4类: (1)位置标度:用于将连续型、离散型和日期-时间型变量映射到绘图区域, 以及构造对应的坐标轴;

(2)颜色标度:用于将连续型和离散型变量映射到颜色;

(3)手动离散型标度:用于将离散型变量映射到我们选择的符号大小、线条类型、形状或颜色, 以及创建对应的图例; 以及(4)同一型标度:用于直接将变量值绘制为图形属性, 而不去映射他们。

实际应用中修改标度最长用的有3个方面 (1) 修改图例 (2) 修改图形属性 (3) 修改坐标轴

介于标度内容的复杂性, 建议详细的参考如下链接: (1)索引中有关scale的内容: http://docs.ggplot2.org/current/index.html

(2)cookbook中有关图例的修改: http://www.cookbook-r.com/Graphs/Legends_(ggplot2)/

(3)cookbook中有关坐标轴的修改: http://www.cookbook-r.com/Graphs/Axes_(ggplot2)/

(4)ColorBrewers配色方案: http://colorbrewer.org

在后面的实例中, 每一张完美的图都需要对其标度进行细致的修改。

6. 坐标系(coord)

coordinate 常用的是coord_cartesian,用法是coord_cartesian(xlim = c(xmin, xmax), ylim = c(ymin, ymax))。coordinate系统主要控制坐标轴,告诉ggplot坐标轴上取值的范围。还是以上图为例,把y轴的范围设置成0到80:

ggplot(mpg, aes(class)) +
  geom_bar() +
  coord_cartesian(ylim = c(0, 80))

7. 分面(facet)

分面和图层将原数据切割称多个小数据集,即每个图层的每个分面面板都含有一个小数据集。

你可以把它想象成一个三维矩阵:分面面板形成了一个2维网格,图层在第三维的方向上叠加。

分面这个概念使得分组对比图的绘制非常方便。

而图层的概念使得我们用R绘图跟艺术家绘图别无二致。

如gglot()函数如同开启了一块画布,通过“+”叠加的几何对象:简称geom,控制生成的图像类型,如geom_line(),geom_point(),可以进行图层的叠加,也就相当于是层层绘制,把同一来源的数据通过不同的图形类型展示在同一幅图中,或者把不同来源的数据很方便的展示在同一个图形对象中。也可以理解成每一块画布是透明的,通过画布的叠加来丰富我们的图形对象。

分面(facet)即在一个页面上自动摆放多幅图形, 这一过程先将数据划分为多个子集, 然后将每个子集依次绘制到页面的不同面板中。

ggplot2提供两种分面类型:网格型(facet_grid)和封装封面型(facet_wrap)。

网格分面生成的是一个2维的面板网格, 面板的行与列通过变量来定义, 本质是2维的;

封装分面则先生成一个1维的面板条块, 然后再分装到2维中, 本质是1维的。

在很多情况下, 我们可能需要绘制有两个y轴的坐标系, 而在ggplot2中, 这种做法特别不提倡stackover的讨论, 可解决的方法要么是把变量归一化, 要么便是采用分面方法。

p <- ggplot(mtcars, aes(mpg, wt)) + geom_point()

#以cyl为分类变量,按列排
p + facet_grid(. ~ cyl) 

#wrap与grid的区别
p + facet_wrap( ~ cyl, nrow = 3)

#以cyl为分类变量
p + facet_grid(cyl ~ .) 

#wrap与grid的区别
p + facet_wrap( ~ cyl, ncol = 3) 
# p + facet_wrap( ~ cyl, nrow = 3) 

#行以vs和列以am为分类变量
p + facet_grid(vs ~ am)

p + facet_wrap(vs ~ am, ncol = 2) #wrap与grid 的区别

facet另外一个案例margins

p <- p + geom_smooth(method = "lm", se =F, aes(color = factor(cyl))) + 
  geom_point(aes(color = factor(cyl)))
p + facet_grid(vs ~ am)

#使用margins来描述边际图
p + facet_grid(vs ~ am, margins = T) 

p + facet_grid( ~ cyl, scales = "free")
p + facet_grid( ~ cyl, scales = "free_x")

facet另外一个案例scales

p <- ggplot(aes(cty, hwy), data = mpg) + geom_point()

#调整scales的标度, 共有fixed, free, free_x和free_y四种变换
p + facet_wrap( ~ cyl)

# 这里标度更改为free
p + facet_wrap( ~ cyl, scales = "free") 

#space设置为free时, 每列的宽度与该列的标度范围成比例
p + facet_grid(. ~ cyl, scales = "free", space = "free") 

##使用自由标度来替代 双坐标轴 的实战的一个例子

facet另外一个案例 facet可以把一张图按变量分割成若干小图。下面的例子用facet_wrap按生产商分车型:

facet_example <- ggplot(mpg, aes(class)) +
  geom_bar() +
  facet_wrap(~manufacturer)

facet_example

8. 主题(theme)调整

主题系统控制着图形中的非数据元素外观, 它不会影响几何对象和标度等数据元素。主题修改是一个对绘图精雕细琢的过程, 主要对标题、坐标轴标签、图例标签等文字调整, 以及网格线、背景、轴须的颜色搭配。

rating <- runif(1000,min = 1, max = 10)
id <- 1:1000
movies <- data.frame(id, rating)

p <- ggplot(movies,  aes(x = rating)) + geom_histogram(binwidth = 1)

#白色背景
p + theme_bw()

#默认浅灰色背景
p + theme_grey() 

#classic, white background
classicTheme <- ggplot(mpg, aes(class)) +
              geom_bar() +
              theme_classic()
classicTheme

主题由控制图形外观的多个元素组成, 详见官方索引

##element_text()修改标签和标题
p <- p + labs(title = "histogram")
p + theme(
  plot.title = element_text(size = 20, 
                            color = "red", 
                            hjust = 0, 
                            face = "bold", 
                            angle = 180))

内置元素共有四个基础类型:文本(text), 线条(line)、矩形(rectangle)、空白(blank), text与其他类型操作相类似, 具体的例子可参考索引, 此处用element_blank()来去除灰色背景。

p + theme(panel.background = element_blank()) #blank是去掉某种绘图元素

一个丧心病狂的主题设置

ggplot(mpg, aes(class)) +
  geom_bar() +
  theme(panel.background = element_blank(),
        legend.key = element_blank(),
        legend.background = element_blank(),
        strip.background = element_blank(),
        plot.background = element_rect(fill = "#FEE0D2", color = "black", size = 3),
        panel.grid = element_blank(),
        axis.line = element_line(color = "black"),
        axis.ticks = element_line(color = "black"),
        strip.text = element_text(size = 16, color = "#99000D"),
        axis.title.y = element_text(color = "#99000D", hjust = 0, face = "italic"),
        axis.title.x = element_text(color = "#99000D", hjust = 0, face = "italic"),
        axis.text = element_text(color = "black"),
        legend.position = "none")

调整的学问

这里的调整主要是使用微调图形这大类的函数做美学特征、坐标轴、标题、绘图主题的调整。这部分也就是继承了命令式作图的思想,使ggplot2的灵活性增加。

如何搜索你要用什么美学特征调整函数,其实就是按照美学特征的名字来,例如,你要调整的是fill,就找scale_fill_之后就有一些不同的染色方法(关于色彩,如果有时间还会添加相关知识);调整的是横坐标标尺,就找scale_x_然后后面跟上你的横坐标类型;其他雷同。

在调整主题这方面,值得褒奖的是,theme函数其实最妙的地方是将对于数据相关的美学调整和与数据无关的美学调整分离了。

譬如说,我们要改变x轴的颜色,或者panel的底色,这个其实与数据处理无关,这种分离就会使得我们可以如此流程化地操作作图,而不需要在考虑数据的时候还要关注到与数据无关的美学参数。

有人有时候会觉得ggplot2很奇怪的地方就是为什么调整legend的时候,有时要用scale_,有时又要用theme,其实这都是对于ggplot2这个设计理念的不理解,作者的设计思路是要将数据处理与数据美学分开,数据美学与数据无关的调整分开。其次,theme函数采用了四个简单地函数来调整所有的主题特征:

element_text调整字体, element_line调整主题内的所有线, element_rect调整所有的块, element_blank清空。

这种设计相当地棒。由此,一个极具诚意的作图应该长成下面这个样子:

ggplot(data = , aes(x = , y = )) + 
    geom_XXX(...) + ... + stat_XXX(...) + ... +
    annotate(...) + ... +
    scale_XXX(...) + coord_XXX(...) + guides(...) + theme(...)

9. 存储和输出ggsave

ggsave()是ggplot2种特有的输出函数, 是一种极为方便的出图方式

p <- ggplot(mtcars, aes(x = mpg, y = disp)) + geom_point()

ggsave( file = "mtcars_plot.png", 
        width = 5, 
        height = 6, 
        type = "cairo", 
        dpi = 600) 
#cairo为抗锯齿包, ggplot默认输出即为cairo处理

ggplot2支持eps矢量图输出, 其他可以支持的格式包括png, jpg, pdf等, 并通过ggsave可以方便的进行修改。

高质量图片输出

绘图完成后最后一步便是图片输出,高质量的图片输出让人赏心悦目,而不正确的输出方式或者直接采用截图的方式从图形设备中截取,得到的图片往往是低劣的。

一幅高质量的图片应当控制图片尺寸和字体大小,并对矢量图进行高质量渲染,即所谓的抗锯齿。

R语言通过支持Cairo矢量图形处理的类库,可以创建高质量的矢量图形(PDF,PostScript,SVG) 和 位图(PNG,JPEG, TIFF),同时支持在后台程序中高质量渲染。

在ggplot2我比较推荐的图片输出格式为经过Cairo包处理的PDF,因为PDF格式体积小,同时可以储存为其他任何格式,随后再将PDF储存为eps格式并在Photoshop中打开做最终的调整,例如调整比例、色彩空间和dpi(一般杂志和出版社要求dpi=300以上)等。

额外需要注意的是ggplot2中的字体大小问题,在cookbook-r一书中指出,在ggplot2中绝大多数情况下,size的大小以mm记,详细的讨论也可以参考stackover的讨论.

而在theme()中对element_text()里的size进行调整,此时的size是以磅值(points, pts)来进行表示。

下面以3种ggplot2种常用的图片输出方式,输出一幅主标题为20pts,横纵坐标标题为15pts,长为80mm(3.15in),宽为60mm(2.36in)的图为例。

require(ggplot2)
require(Cairo)

ggplot() +
  geom_text(aes(x = 16, y = 16), label = "ABC", size = 11.28) + #尺寸为11.28mm,即为32磅
  geom_text(aes(x = 16, y = 14.5), label = "ABC", size = 32) + #尺寸为32mm
  labs( x = "x axis", y = "y axis") +
  ylim( c(14, 16.5)) +
  xlim( c(15.75, 16.25)) +
  theme(
    axis.title.x = element_text(size = 32),#尺寸为32磅
    axis.title.y = element_text(size = 32))#尺寸为32磅
 
x <- seq(-4,4, length.out = 1000)
y <-dnorm(x)
data <- data.frame(x, y)
 
#用Cairo包输出
require(Cairo)
CairoPDF("plot1.pdf", 3.15, 3.15) #单位为英寸
ggplot(data, aes(x = x, y = y)) + geom_line(size = 1) +
  theme_bw()
dev.off() #关闭图像设备,同时储存图片
 
plot2 <- ggplot(data, aes(x = x, y = y)) + geom_line(size = 1) +
  theme_bw()
plot2
#用ggsave输出,默认即以用Cairo包进行抗锯齿处理
ggsave("plot2.pdf", plot2, width = 3.15, height = 3.15) 
 
#RStudio输出

更改字体

更改默认字体或者采用中文输出图片是十分恼人的一件事情,好在我们还有各种拓展包和功能强大的Rstudio来实现。

用extrafont输出英文字体 extrafont包能够直接调用字体文件,再通过Ghostscript(需要安装)将写入的字体插入生成的PDF中,具体代码可参考了作者说明extrafont

好玩的showtext 邱怡轩大神写了一个好玩的showtext,确实好好玩~

简单易用的RStudio输出 最简单实用的输出方法还是使用RStudio输出,直接调用系统字体(我的是win7,mac和linux下还没有试过)并输出即可

#showtext
# Load package
if(!suppressWarnings(require("showtext")))
{
    install.packages("showtext")
    require("showtext")
}

require(showtext)
require(ggplot2)
require(Cairo)
# font.add("BlackoakStd", "C://Windows//Fonts//BlackoakStd.otf")
font.add("BRUSHSCI", "C://Windows//Fonts//BRUSHSCI.TTF")
font.add("times", "C://Windows//Fonts//times.ttf")
font.add("STHUPO", "C://Windows//Fonts//STHUPO.ttf")
CairoPDF("showtext_output", 8, 8)
showtext.begin()
ggplot() +
  geom_text(aes(x = 16, y = 16.25), label = "Blackoak Std", size = 8, 
            family = "BlackoakStd") +
  geom_text(aes(x = 16, y = 16), label ="Brush Script Std", size = 16,
            family = "BrushScriptStd") +
  geom_text(aes(x = 16, y = 15.75), label = "Times New Roman", size = 16,
            family = "times") +
  geom_text(aes(x = 16, y = 15.50), label = "华文琥珀", size = 16,
            family = "STHUPO") +
  ylim(c(15.25, 16.50)) +
  labs(x = "", y = "") +
  theme_bw() #在用RStudio输出

10 ggplot2作图实战

1. 时间序列图

ggplot_timeseries_data.csv

#用excel导入数据, 格式为csv
ori.data <- read.csv("E:/Github/xiangxing98.github.io/R_Learning/ggplot_timeseries_data.csv", header = F)
head(ori.data)
#                             V1
# 1 Sun Jul  8 23:59:02 HKT 2012
# 2                         1922
# 3                        91938
# 4 Mon Jul  9 23:59:01 HKT 2012
# 5                         2345
# 6                       108521

#以矩阵的方式读入数据, 按行排列, 每三列换一行,时间,IP,PV
data <- matrix(as.matrix(ori.data), nrow(ori.data) / 3, 3, byrow = TRUE)
head(data)
#      [,1]                           [,2]   [,3]    
# [1,] "Sun Jul  8 23:59:02 HKT 2012" "1922" "91938" 
# [2,] "Mon Jul  9 23:59:01 HKT 2012" "2345" "108521"
# [3,] "Tue Jul 10 23:59:01 HKT 2012" "2255" "89036" 
# [4,] "Wed Jul 11 23:59:01 HKT 2012" "2179" "84149" 
# [5,] "Thu Jul 12 23:59:02 HKT 2012" "2225" "85583" 
# [6,] "Fri Jul 13 23:59:01 HKT 2012" "2392" "79507" 


# Get Sys.getlocale for recovery, use Sys.setlocale("LC_TIME", lct)
lct <- Sys.getlocale("LC_TIME")

#关闭区域特定的时间编码方式
Sys.setlocale("LC_TIME", "C")
#x <- c("1jan1960", "2jan1960", "31mar1960", "30jul1960")
#z <- strptime(x, "%d%b%Y")
#z

#用as.POSIXlt()读入字符串数据并转化为date数据, 赋值给date, 或as.Date()
date <- as.POSIXlt(data[, 1], tz = "", "%a %b %d %H:%M:%S HKT %Y")
#date <- strptime(data[, 1], "%a %b %d %H:%M:%S HKT %Y")
#strptime("Fri Jul 13 23:59:01 HKT 2012", "%a %b %d %H:%M:%S HKT %Y")
# Fri Jul 13 23:59:01 HKT 2012
#strptime("Tue, 23 Mar 2010 14:36:38 -0400",  "%a, %d %b %Y %H:%M:%S %z")

#check data
head(data)

#对ip和pv所在的列转化为数值型
IP <- as.numeric(data[, 2])
PV <- as.numeric(data[, 3])

#恢复区域特地的时间编码方式
Sys.setlocale("LC_TIME", lct)

#用ggplot2绘图
require(ggplot2)
#用reshape包中的melt函数分解数据
require(reshape2)

p.data <- data.frame(date, IP, PV)
# 融合数据,只留下日期列,变量分组列以及对应的数值
meltdata <- melt(p.data, id = (c("date")))
#check meltdata
head(meltdata);tail(meltdata)
#                  date variable value
# 1 2012-07-08 23:59:02       IP  1922
# 2 2012-07-09 23:59:01       IP  2345
# 3 2012-07-10 23:59:01       IP  2255
# 4 2012-07-11 23:59:01       IP  2179
# 5 2012-07-12 23:59:02       IP  2225
# 6 2012-07-13 23:59:01       IP  2392
#                    date variable  value
# 161 2012-09-23 23:59:01       PV 118785
# 162 2012-09-24 23:59:01       PV 144766
# 163 2012-09-25 23:59:01       PV 120141
# 164 2012-09-26 23:59:01       PV 115623
# 165 2012-09-27 23:59:01       PV 118966
# 166 2012-09-28 23:59:02       PV 132440

#用对IP和PV做分页处理, y轴刻度自由变化
graphic <- ggplot(data = meltdata, 
                  aes(x = date, y = value, color = variable)) + 
            geom_line() + 
            geom_point()
# 分页
graphic <- graphic + facet_grid(variable ~ ., scales = "free_y")
# graphic

#美化, 添加标题, 坐标, 更改图例
graphic<- graphic + labs(x = "日期", y = "人次", title = "某网站7月至10月IP/PV统计") +
  theme(plot.title = element_text(size = 20, face = "bold")) +
  scale_colour_discrete(name = "",labels = c("IP","PV")) +
  theme(strip.text.y = element_text(angle = 0))

# check the graphic
graphic

2. 人口分布图-结合地图-Draw City Population Map

require(maps)
require(ggplot2)
#用直方图看下pop整体的分布
#可以发现数据分布较变化较大, 所以对pop做log转化
qplot(pop, data = us.cities, binwidth = 0000, geom = "histogram")
qplot(log(pop), data = us.cities, binwidth = 0.03, geom = "histogram")
 
#绘制背景地图
USA.POP <- ggplot(us.cities, aes(x = long, y = lat)) + xlim(-130, -65) + borders("state", size=0.5)+
  geom_point(aes(size = log(pop), color = factor(capital), alpha = 1/50))+
  #对size标度的调整参考http://docs.ggplot2.org/0.9.3.1/scale_size.html
  scale_size(range=c(0, 7), name = "log(City population)")+
  #对离散型颜色变量的标度调整参考http://docs.ggplot2.org/0.9.3.1/scale_manual.html
  #对连续型颜色标量的标度调整参考http://docs.ggplot2.org/0.9.3.1/scale_brewer.html
  #和http://docs.ggplot2.org/0.9.3.1/scale_gradient2.html
  scale_color_manual(values = c("black", "red"), labels = c("state capital", "city"))+
  #调整图例
  guides(color = guide_legend(title=NULL)) + scale_alpha(guide = FALSE)+
  #绘制标题和坐标轴
  labs(x = "longtitude", y = "latitude", title = "City Population in the United States")+
  theme(plot.title = element_text(size=20))

USA.POP

#输出图像 并用cairo包进行抗锯齿处理
ggsave(USA.POP, file = "E:/Github/xiangxing98.github.io/R_Learning/USA_POP.png", type = "cairo", width = 10, height = 6.75)

当然, 这只是简单的地图绘制方法,统计之都上也有很多大牛来用R绘制各种各样精美的地图(1Map, 2visualizing-flights-data

3. 剂量-效应曲线图-Draw dose-effect plot

R中的drc包http://www.bioassay.dk/index-filer/start/DraftDrcManual.pdf很容易对各种剂量-效应曲线进行绘图, 此处采用较为常用的log-logistic四参数方程拟合了剂量-效应曲线。

ori.data <- read.csv("E:/Github/xiangxing98.github.io/R_Learning/D-R_curve.csv")
require(drc)
require(reshape2)
#把数据融合
melt.data <- melt(ori.data, id = c("dose"), value.name = "response")[, -2]
#用drc包中的log-logistic四参数方程进行拟合建模
model <- drm(response ~ dose, data = melt.data, fct = LL.4(names = c("Slope", "Lower Limit", "Upper Limit", "EC50")))
#确定x轴范围并构建数据集
min <- range(ori.data$dose)[1]
max <- range(ori.data$dose)[2]
line.data <- data.frame(d.predict = seq(min, max, length.out = 1000))
#用模型预测数据构建数据集
line.data$p.predict <- predict(model, newdata = line.data)
#构建绘图数据, 能够计算误差棒
require(plyr)
p.data <- ddply(melt.data, .(dose), colwise(mean))
p.data$sd <- ddply(melt.data, .(dose), colwise(sd))[,2]
 
require(ggplot2)
p <- ggplot() +
  geom_errorbar(data = p.data, width = 0.1, size = 1,
                aes(ymax = response + sd, ymin = response - sd, x = dose)) +
  geom_point(data = p.data, aes(x = dose, y = response), 
             color = "red", alpha = 0.5, size = 5) +
  geom_line(data = line.data, aes(x = d.predict, y = p.predict), 
              size = 1, color = "blue") +
  #改变坐标轴间隔
  scale_x_log10(name = "Dose",
                breaks=c(0.05, 0.1, 0.5, 1, 5, 10, 50, 100)) +
  scale_y_continuous(name = "Response") +
  theme_bw()
p
#查看拟合模型参数
summary(model)

4. 乳房曲线图-Breast Curve–Baybe’s favorite

##用ggplot2来画函数
library(ggplot2)
#确定x轴区域
f <- ggplot(data.frame(x = c(0.00001, 1)), 
            aes(x, color="pink", size=2))
breast_curve <- function(x) {(1/36)*exp(-((36*x-36/2.71828182845905)^4))-3*x*log10(x)}
f <- f + stat_function(fun = breast_curve) + 
  theme(legend.position="none") +
  #旋转坐标轴
  coord_flip()
f

5. Michaelis-Menten动力学方程

how_to_use_gggplot2_part2/

这个例子中采用出自文献中的一组有关于浮萍氮摄取的数据,共2两个变量8个观测值,其中底物浓度与浮萍的氮取速率之间可以通过M-M动力学方程来进行描述。在这个例子中首先通过nls()根据M-M动力学方程进行模型拟合,然后用预测值进行了ggplot2绘图,主要采用了R里面的数学表示方法plotmath在图中展示了公式,并通过ggplot2种的theme对图像进行了修饰。需要注意的在geom_text()并不能直接使用expression,需要开启parse = TURE,且用字符串表示。

conc <- c(2.856829, 5.005303, 7.519473, 22.101664, 27.769976, 39.198025, 45.483269, 203.784238)
rate <- c(14.58342, 24.74123, 31.34551, 72.96985, 77.50099, 96.08794, 96.96624, 108.88374)
L.minor <- data.frame(conc, rate)
L.minor.m1 <- nls(rate ~ Vm * conc/(K + conc), data = L.minor, #采用M-M动力学方程
                  start = list(K = 20, Vm = 120), #初始值设置为K=20,Vm=120
                  trace = TRUE) #占线拟合过程
#确定x轴范围并构建数据集
min <- range(L.minor$conc)[1]
max <- range(L.minor$conc)[2]
line.data <- data.frame(conc = seq(min, max, length.out = 1000))
#用模型预测数据构建数据集
line.data$p.predict <- predict(L.minor.m1, newdata = line.data)
 
require(ggplot2)
M_Mfunction <- ggplot() +
  geom_point(aes(x = conc, y = rate), data = L.minor,
             alpha = 0.5, size = 5, color = "red") +
  geom_line(aes(x = conc, y = p.predict), data = line.data,
            size = 1, color = "blue") +
  scale_x_continuous(
    name = expression(Substrate ~~ concentration(mmol ~~ m^3)),#采用expression来表示数学公式
    breaks = seq(0, 200, by = 25)) +
  scale_y_continuous(
    name = "Uptake rate (weight/h)",
    breaks = seq(0, 120, by = 10)) +
  geom_text(aes(x = 100, y = 60),
            label = "bolditalic(f(list(x, (list(K, V[m])))) == frac(V[m]%.%x, K+x))",
            #注意 geom_text中如果用expression()来进行表达,必须开启parse = TRUE
            #同时以字符串""的形式表示,不能使用expression
            parse = TRUE, 
            size = 5, family = "times"
            ) +
  theme_bw() +
  theme(
        axis.title.x=element_text(size=16),
        axis.title.y=element_text(size=16),
        axis.text.x=element_text(size=12),
        axis.text.y=element_text(size=12))
M_Mfunction

6. 热图-heatmap

热图是一种极好的数据可视化方式,能够清楚的显示出多维数据之间的关联性和差异性,糗世界已经为我们展现了R里面所常用的heatmap,ggplot2和lattice3种热图绘制方式,当然随着R的不断进步,已经有多种包提供了更丰富和更简单的热图绘制方式,例如gplots中的heatmap.2,pheatmap,heatmap.plus等等。ggplot2进行热图的绘制也十分方便,热图的关键是聚类,两个可行的方案是对聚类结果进行排序和将聚类结果因子化后固定,通过结合plyr包,可以很方便的实现。这里采用一组来源于WHO国家数据来对热图的绘制进行,首先数据标准化和正态化后按Index的D(为各国的人口数据)进行排序,再将其因子化后固定,用geom_tile()进行热图的绘制,在ggplot2种已能通过scale_fill_gradient2在三种基本色进行渐变。

WHO <- read.csv("E:/Github/xiangxing98.github.io/R_Learning/WHO.csv", header = TRUE)
require(plyr)
#按总人口数排列数据
WHO <- arrange(WHO, desc(D))
#将数据的名字转换为因子,并固定已拍好的country,
#同理可以按照聚类的结果进行排列
WHO <- transform(WHO, Country = factor(Country, levels = unique(Country)))
 
require(reshape2)
require(ggplot2)
require(scales)
require(grid)

#melt数据
m.WHO <- melt(WHO)
#标准化,每排数据映射到按最小值和最大值映射到(0,1)区间
m.WHO <- ddply(m.WHO, .(variable), transform, rescale = rescale(value))
#标准化并正态化数据
s.WHO <- ddply(m.WHO, .(variable), transform, rescale = scale(value))
require(ggplot2)
p <- ggplot(s.WHO, aes(variable, Country)) +
  #用tile来进行绘热力图
  geom_tile(aes(fill=rescale)) +
  scale_fill_gradient2(mid="black", high="red", low="green", name = "Intensity") +
  labs(x="Country", y="Index", face = "bold") +
  theme_bw() +
  theme(
    axis.title.x=element_text(size=16),
    axis.title.y=element_text(size=16),
    axis.text.x=element_text(size=12, colour="grey50"),
    axis.text.y=element_text(size=12, colour="grey50"),
    legend.title=element_text(size=14),
    legend.text=element_text(size=12),
    legend.key.size = unit(0.8, "cm"))#需要载入grid包来调整legend的大小

p

7. 火山图vacano map

火山图是散点图的一种,能够快速的辨别出大型数据集重复变量之间的差异,具体的介绍可以参考wiki和Colin Gillespie的博客,下面的代码和图是使用ggplot2的实现方式。

require(ggplot2)
##change theme##
old_theme <- theme_update(
  axis.ticks=element_line(colour="black"),
  panel.grid.major=element_blank(),
  panel.grid.minor=element_blank(),
  panel.background=element_blank(),
  axis.line=element_line(size=0.5)
)

##Highlight genes that have an absolute fold change > 2 and a p-value < Bonferroni cut-off
a <- read.table("E:/Github/xiangxing98.github.io/R_Learning/flu.txt",header=TRUE,sep="\t")
P.Value <- c(a$P.Value)
FC <- c(a$FC)
df <- data.frame(P.Value, FC)
df.G <- subset(df, log2(FC) < -1& P.Value < 0.05) #define Green
df.G <- cbind(df.G, rep(1, nrow(df.G)))
colnames(df.G)[3] <- "Color"
df.B <- subset(df, (log2(FC) >= -1 & log2(FC) <= 1) | P.Value >= 0.05) #define Black
df.B <- cbind(df.B, rep(2, nrow(df.B)))
colnames(df.B)[3] <- "Color"
df.R <- subset(df, log2(FC) > 1 & P.Value < 0.05) #define Red
df.R <- cbind(df.R, rep(3, nrow(df.R)))
colnames(df.R)[3] <- "Color"
df.t <- rbind(df.G, df.B, df.R)
df.t$Color <- as.factor(df.t$Color)
##Construct the plot object
ggplot(data = df.t, aes(x = log2(FC), y = -log10(P.Value), color= Color )) +
  geom_point(alpha = 0.5, size = 1.75) +
  theme( legend.position = "none") +
  xlim(c(-5, 5)) + ylim(c(0, 20)) +
  scale_color_manual(values = c("green", "black", "red")) +
  labs(x=expression(log[2](FC)), y=expression( -log[10](P.Value))) +
  theme(axis.title.x=element_text(size=20), 
        axis.text.x=element_text(size=15)) +
  theme(axis.title.y=element_text(size=20),
        axis.text.y=element_text(size=15))

8. 散点图scatter plot

scatter <-
  ggplot(mtcars, aes(
    x = wt,
    y = mpg,
    color = as.factor(cyl),
    shape = as.factor(cyl)
  )) +
  geom_point() +
  geom_smooth(method = lm, se = FALSE, fullrange = TRUE) +
  labs(title = "Miles per gallon \n according to the weight",
  x = "Weight (lb/1000)", y = "Miles/(US) gallon") +
  theme_classic() +
  scale_color_brewer(palette = "Accent") +
  theme_minimal()
scatter

scatter plot 2

scatterplot2 <- ggplot(mpg, aes(cty, hwy)) +
                geom_point() +
                geom_smooth()

scatterplot2

9. Diamonds

require("ggplot2")
p <- ggplot(data = diamonds,
            aes(x = carat, y = price, colour = cut))

# add layer function 1 layer
p1 <- p + layer(geom = "point", stat = "identity", position = "dodge")
p1

# quick add layer
p + geom_point()

10. 直方图-Bar plot

library(ggplot2)
library(gridExtra)
hist1 <- ggplot(data = diamonds, aes(x = clarity, fill = cut))+
  geom_histogram(position = "dodge", stat="count")
hist1

hist2 <- ggplot(data = diamonds, aes(x = clarity, fill = cut))+
  geom_histogram(position = "fill", stat="count")
hist2

hist3 <- ggplot(data = diamonds, aes(x = clarity, fill = cut))+
  geom_histogram(position = "stack", stat="count")
hist3

grid.arrange(hist1,hist2,hist3, ncol = 3, nrow = 1)

bar plot example 2

d<-ggplot(data = diamonds, aes(x = carat))
d1<-d+
  stat_bin(aes(ymax = ..count..), binwidth = 0.1,geom = "area")
d2<-d+
  stat_bin(aes(size = ..density..),binwidth = 0.1, geom = "point",position = "identity")

grid.arrange(d1, d2, ncol = 2, nrow = 1)

bar plot example 3

barplot3 <- ggplot(mpg, aes(class)) +
            geom_bar()

barplot3 

# color group manufacturer
barplot4 <- ggplot(mpg, aes(class, fill = manufacturer)) +
            geom_bar()
barplot4


color <- ggplot(mpg, aes(cty, hwy, color = class)) + 
          geom_point()
color

size <- ggplot(mpg, aes(cty, hwy, size = class)) + 
          geom_point()
size

shape <- ggplot(mpg, aes(cty, hwy, shape = class)) +
          geom_point()
shape

11. 将不同来源的数据画在同一张图上

例如用模型拟合出来的预测值扩充原始数据

#例子来源于stackoverflow
df1<-data.frame(x=1:10,y=rnorm(10))
df2<-data.frame(x=1:10,y=rnorm(10))

Draw_add_data <- ggplot(df1,aes(x,y)) + 
            geom_line(aes(color="First line")) + 
            geom_line(data=df2,aes(color="Second line")) + 
            labs(color="Legend text")
Draw_add_data

我很喜欢的一个例子来自Wickham,在同一张图上画了美国的历史失业率和总统任期:

presidential <- subset(presidential, start > economics$date[1])
ggplot(economics) + #第一个数据集economics
  geom_rect( #用第二个数据集presidential画长方形(rectangle),用不同颜色表示不同党派
  aes(xmin = start, xmax = end, fill = party), ymin = -Inf, ymax = Inf, alpha = 0.2,
  data = presidential #数据和第一层所用的数据不一样,所以要明确data = ...
  ) + 
  geom_vline( #还是用presidential在每位总统上任时间画竖线(vertical line)
  aes(xintercept = as.numeric(start)), 
  data = presidential,
  colour = "grey50", alpha = 0.5
  ) + 
  geom_text( #仍然是用presidential在每段任期内y轴2500的地方“画”出总统的名字
  aes(x = start, y = 2500, label = name),
  data = presidential,
  size = 3, vjust = 0, hjust = 0, nudge_x = 50
  ) +
  geom_line(aes(date, unemploy)) + #以时间和失业人数作为x轴和y轴画折线图
  scale_fill_manual(values = c("blue", "red")) #手动设置填充颜色

12. ggplot2 plot sequence 堆积木

推荐一个好的ggplot2的做图流程 1. 数据导入,坐标轴映射(指定谁横着站在X,谁竖着站在Y) 2. 竖着站的数值如何用图形表示,是不是还要做一些变换 3. 添加一些注释说明,解释一些异常? 4. 坐标刻度、轴怎么设置 5. guides是什么? 6. 主题微调美化 ggplot(data = , aes(x = , y = )) + geom_XXX(…) + … + stat_XXX(…) + … + annotate(…) + … + scale_XXX(…) + coord_XXX(…) + guides(…) + theme(…)

## 先设定默认的数据与默认的图形属性映射(属性为点的x轴和y轴坐标位置)
p <- ggplot(dsmall) + aes (carat , price)

## 开始填加几何对象
p+ geom_point()
p+ geom_point() +geom_smooth()

## 开始填加几何对象的属性(颜色来表示信息)
p+ geom_point(aes(colour=color) )
p+ geom_point(aes(colour=cut) )
p+ geom_text(aes(label=color))

## 改变一下y轴表示的信息
p+ geom_point ( aes ( y = log ( price) ) )

## 分组的线图
p <- ggplot(Oxboys, aes(age,height, group= Subject)) + geom_line()
p
p + geom_smooth(aes(group=1), method = "lm", size = 2, se=F)

13. ggplot2 change data set to draw plot

使用ggplot2还可以更灵活地使用多个数据集,如下面的例子。

## 更改一个数据作图,只需要%+%
p <- ggplot(dsmall, aes(carat, price))+ geom_point()
p %+% diamonds

# 可以方便地加入标题与元数据
## 加上标题(加上labs)
p+labs(title="这是一个图")

## 标注出价格最高的那个点(再加上一个geom_point)
highest = subset (dsmall , price == max (price))
p + geom_point(data=highest, size=6, colour= "red",alpha=0.5)

## 主题的使用
p + theme_grey()
p + theme_bw()
p + theme_classic()

library("ggthemes")
p + theme_stata()

Add Title and xlab/ylab

plot title/x lab/y lab

title_xlab_ylab <- ggplot(mpg, aes(cty, hwy)) +
  geom_point() +
  xlab("city miles per gallon") + #x轴名称
  ylab("highway miles per gallon") + #y轴名称
  ggtitle("city vs. highway miles per gallon") #图标标题
title_xlab_ylab

Day 1 Reference book

内容是基于这本书ggplot2: Elegant Graphics for Data Analysis

另外据说这本也是个不错的参考R Graphics Cookbook

下面是ggplot2的一些文档和github上的源代码 http://docs.ggplot2.org/current/ https://github.com/hadley/ggplot2

本篇文章涉及ggplot2: Elegant Graphics for Data Analysis 中的第二章 在正式开始学习ggplot2命令之前 我们首先讨论qplot qplot是quick plot的缩写 旨在用最简短的命令画出我们所需的图

1. 数据

首先在R中安装ggplot2 and Import Data

# Load ggplot2 package
if(!suppressWarnings(require(ggplot2)))
{
    install.packages('ggplot2')
    require(ggplot2)
}

# Read data
X <- read.delim("http://www.stat.ubc.ca/~rickw/gapminderDataFiveYear.txt")

# output as csv data
write.csv(X,file = "E:\\Github\\xiangxing98.github.io\\R_Learning\\gapminderDataFiveYear.csv", row.names = TRUE)


# Check Data X structure
str(X)
# 'data.frame': 1704 obs. of  6 variables:
#  $ country  : Factor w/ 142 levels "Afghanistan",..: 1 1 1 1 1 1 1 1 1 1 ...
#  $ year     : int  1952 1957 1962 1967 1972 1977 1982 1987 1992 1997 ...
#  $ pop      : num  8425333 9240934 10267083 11537966 13079460 ...
#  $ continent: Factor w/ 5 levels "Africa","Americas",..: 3 3 3 3 3 3 3 3 3 3 ...
#  $ lifeExp  : num  28.8 30.3 32 34 36.1 ...
#  $ gdpPercap: num  779 821 853 836 740 ...

2. qplot基本用法

qplot最简单的使用方法和R base中的plot基本一样 可以用来画散点图

qplot(gdpPercap, lifeExp, data=X)

使用 color = year 可以将该变量用不同颜色标示出来 这里year是个连续的变量 所以颜色以谱的形式标示 用 log = “x” 表示对横轴的变量进行log变换

qplot(gdpPercap, lifeExp, data=X, log = "x", color = year)

也可以将year变成factor 此时会用离散的颜色标示

qplot(gdpPercap, lifeExp, data=X, log = "x", color = factor(year))

使用 size = pop 用标志的大小显示该变量的大小

qplot(gdpPercap, lifeExp, data=X, log = "x", color = year, size = pop)

还可以使用 shape = continent 用不同的标志来显示该变量

qplot(gdpPercap, lifeExp, data=X, log = "x", color = year, shape = continent)

最后 alpha=I(0.25) 指定透明度 0为全透明 1为不透明 当数据重叠严重时比较有用

qplot(gdpPercap, lifeExp, data=X, log = "x", alpha=I(0.25))

3. geom

geom是geometric object的简称 用来生成不同种类的图

3.1 smooth

首先是smooth 用来描述数据的平滑趋势 注意此处 c(“point”, “smooth”) 表示先画point 再画smooth

#smooth line at top
qplot(gdpPercap, lifeExp, data=X, log = "x", alpha=I(0.5), geom=c("point", "smooth"))

相反的 c(“smooth”, “point”) 就是先画smooth 再画point

#point at top
qplot(gdpPercap, lifeExp, data=X, log = "x", alpha=I(0.5), geom=c("smooth", "point"))

除了默认的平滑方法之外 还可以自行指定 比如线性模型

qplot(gdpPercap, lifeExp, data=X, log = "x", alpha=I(0.5), geom=c("point", "smooth"), method=lm)

另外还可以自行指定公式 例如多项式回归

qplot(gdpPercap, lifeExp, data=X, log = "x", alpha=I(0.5), geom=c("point", "smooth"), method=lm, formula = y ~ poly(x, 3))

3.2 line和path

line会将数据沿横轴方向按顺序连接起来 一般用来表示时间序列数据

qplot(pop, lifeExp, data=X, log = "x", alpha=I(0.5), color=year, geom="line")

path会将原始数据中相邻的两个点连接起来 一般用来表示二维数据随时间的变化

qplot(gdpPercap, lifeExp, data=X, log = "x", alpha=I(0.5), color=year, geom=c("point", "path"))

3.3 boxplot和jitter

和R base中的boxplot一样 横轴的数据需要是factor 注意 color=I(“red”) 中的I()是必须的 否则“red”会被当做一个新的factor

X$year.fac <- factor(X$year)
qplot(year.fac, lifeExp, data=X, color=I("red"), geom="boxplot")

jitter和boxplot类似

qplot(year.fac, lifeExp, data=X, color=I("red"), geom="jitter")

3.4 histogram和density

这里使用 fill=continent 将直方图按不同的continent分割开

qplot(lifeExp,data=X, geom="histogram", fill=continent)

density与histogram类似

qplot(lifeExp,data=X, alpha=I(0.5), geom="density", color=continent)

4. facets

使用facets可以将一个图根据一个或两个变量的值分别显示出来 有利于更直观地进行比较 ~左边表示每一行的变量 右边表示每一列的变量 比如 continent~. 根据continent值的不同 将density的图在每一行里显示出来

qplot(lifeExp,data=X, geom="density", facets=continent~.)

qplot(gdpPercap,data=X, geom="density", facets=continent~.)

类似的 facets=year~continent 根据每一行year和每一列continent值的不同 将histogram的图显示出来

qplot(lifeExp,data=X, geom="histogram", facets=year~continent)

R Graphics Cookbook

rggvis package ggplot2 package plotly package

library(gcookbook)
simpledat
LS0tDQp0aXRsZTogIjIwMTcwNiBnZ3Bsb3QyIExlYXJuaW5nIFJvYWQtU3RvbmVfSG91Lm1kIg0Kb3V0cHV0OiANCiAgaHRtbF9ub3RlYm9vazogDQogICAgbnVtYmVyX3NlY3Rpb25zOiBubw0KICAgIHRvYzogeWVzDQogICAgdG9jX2RlcHRoOiA1DQotLS0NCg0KIyBIZXJlIGlzIFIgTm90ZWJvb2sgR3VpZGVsaW5lDQoNClRoaXMgaXMgYW4gW1IgTWFya2Rvd25dKGh0dHA6Ly9ybWFya2Rvd24ucnN0dWRpby5jb20pIE5vdGVib29rLiBXaGVuIHlvdSBleGVjdXRlIGNvZGUgd2l0aGluIHRoZSBub3RlYm9vaywgdGhlIHJlc3VsdHMgYXBwZWFyIGJlbmVhdGggdGhlIGNvZGUuIA0KDQpUcnkgZXhlY3V0aW5nIHRoaXMgY2h1bmsgYnkgY2xpY2tpbmcgdGhlICpSdW4qIGJ1dHRvbiB3aXRoaW4gdGhlIGNodW5rIG9yIGJ5IHBsYWNpbmcgeW91ciBjdXJzb3IgaW5zaWRlIGl0IGFuZCBwcmVzc2luZyAqQ3RybCtTaGlmdCtFbnRlciouIA0KDQpgYGB7ciBwbG90Y2FycywgZWNobz1UUlVFfQ0KcGxvdChjYXJzKQ0KYGBgDQoNCmBgYHtyIExvYWQgZ2dwbG90MiBwYWNrYWdlfQ0KIyBMb2FkIGdncGxvdDIgcGFja2FnZQ0KaWYoIXN1cHByZXNzV2FybmluZ3MocmVxdWlyZShnZ3Bsb3QyKSkpDQp7DQogICAgaW5zdGFsbC5wYWNrYWdlcygnZ2dwbG90MicpDQogICAgcmVxdWlyZShnZ3Bsb3QyKQ0KfQ0KYGBgDQoNCkFkZCBhIG5ldyBjaHVuayBieSBjbGlja2luZyB0aGUgKkluc2VydCBDaHVuayogYnV0dG9uIG9uIHRoZSB0b29sYmFyIG9yIGJ5IHByZXNzaW5nICpDdHJsK0FsdCtJKi4NCg0KV2hlbiB5b3Ugc2F2ZSB0aGUgbm90ZWJvb2ssIGFuIEhUTUwgZmlsZSBjb250YWluaW5nIHRoZSBjb2RlIGFuZCBvdXRwdXQgd2lsbCBiZSBzYXZlZCBhbG9uZ3NpZGUgaXQgKGNsaWNrIHRoZSAqUHJldmlldyogYnV0dG9uIG9yIHByZXNzICpDdHJsK1NoaWZ0K0sqIHRvIHByZXZpZXcgdGhlIEhUTUwgZmlsZSkuDQoNCiMgMjAxNzA2IGdncGxvdDIgTGVhcm5pbmcgUm9hZC1TdG9uZV9Ib3UubWQNCg0KDQojIyBHcmFtbWFyIG9mIEdyYXBoaWNzKGdnKSBnZ3Bsb3Qy55qE5Zu+5b2i6K+t5rOVDQoNCiMjIyBVbmRlcnN0YW5kaW5nIHRoZSByb290IG9mIGdncGxvdDINCg0KPiBBICBzdGF0aXN0aWNhbCBncmFwaGljIGlzIGEgbWFwcGluZyBmcm9tIGRhdGEgdG8gYWVzdGhldGljIGF0dHJpYnV0ZXMgKGNvbG91cixzaGFwZSwgc2l6ZSkgb2YgZ2VvbWV0cmljIG9iamVjdHMgKHBvaW50cywgbGluZXMsIGJhcnMpLiBCeSBIYW5kbGV5KGF1dGhvciBvZiBkcGx5ciwgcGx5cixzdHJpbmdyLGdncGxvdDIscnZlc3QgcGFja2FnZXMsIGFuZCBvZiBjYXVzZSwgdGlkeSB2ZXJzZShnZ3Bsb3QyIGZvciApKQ0KDQojIyMgdGlkeSB2ZXJzZSBwYWNrYWdlIGZvciBkYXRhIHNjaWVuY2UNCg0KZ2dwbG90MiwgZm9yIGRhdGEgdmlzdWFsaXNhdGlvbi4NCmRwbHlyLCBmb3IgZGF0YSBtYW5pcHVsYXRpb24uDQp0aWR5ciwgZm9yIGRhdGEgdGlkeWluZy4NCnJlYWRyLCBmb3IgZGF0YSBpbXBvcnQuDQpwdXJyciwgZm9yIGZ1bmN0aW9uYWwgcHJvZ3JhbW1pbmcuDQp0aWJibGUsIGZvciB0aWJibGVzLCBhIG1vZGVybiByZS1pbWFnaW5pbmcgb2YgZGF0YSBmcmFtZXMNCg0KQmVmb3JlIHZzIEFmdGVyDQoNCmBgYHtyIGdncGxvdDIgZXhhbXBsZX0NCnggPC0gcm5vcm0oMTAwLDE0LDUpIA0KeSA8LSB4ICsgcm5vcm0oMTAwLDAsMSkgDQpwIDwtIGdncGxvdChkYXRhPSBOVUxMLCBhZXMoeCA9IHgsIHkgPSB5KSkgKyAgI+W8gOWni+e7mOWbvg0KICBnZW9tX3BvaW50KGNvbG9yID0gImRhcmtyZWQiKSArICAj5re75Yqg54K5DQogIGFubm90YXRlKCJ0ZXh0Iix4ID0xMyAsIHkgPSAyMCxwYXJzZSA9IFQsDQogICAgICAgICAgIGxhYmVsID0gInhbMV0gPT0geFsyXSIpICPmt7vliqDms6jph4oNCnANCmBgYA0KDQpnZ3Bsb3Tlm77nmoTlhYPntKDlj6/ku6XkuLvopoHlj6/ku6XmpoLmi6zlpoLkuIvvvJoNCuacgOWkp+eahOaYr3Bsb3TvvIjmjIfmlbTlvKDlm77vvIzljIXmi6xiYWNrZ3JvdW5k5ZKMdGl0bGXvvInvvIwNCg0K5YW25qyh5pivYXhpc++8iOWMheaLrHN0aWNr77yMdGV4dO+8jHRpdGxl5ZKMc3RpY2vvvInjgIFsZWdlbmTvvIjljIXmi6xiYWNrZ3JvdWTjgIF0ZXh044CBdGl0bGXvvInjgIFmYWNldOi/meaYr+esrOS6jOWxguasoe+8jOWFtuS4rWZhY2V05Y+v5Lul5YiG5Li65aSW6YOoc3RyaXDpg6jliIbvvIjljIXmi6xiYWNrZ3JvdWTlkox0ZXh077yJ5ZKM5YaF6YOocGFuZWzpg6jliIbvvIjljIXmi6xiYWNrZ3JvdWTjgIFib2RlcuWSjOe9keagvOe6v2dyaWTvvIzlhbbkuK3nspfnmoTlj6tncmlkLm1ham9y77yM57uG55qE5Y+rZ3JpZC5taW5vcu+8ieOAgg0KDQpnZ3Bsb3Qy6aOO5qC855qE57uY5Zu+55qE56ys5LiA5q2l5bCx5piv5Yid5aeL5YyW77yM6K+055m95LqG5bCx5piv6L295YWl5pWw5o2u56m66Ze044CB6YCJ5oup5pWw5o2u5Lul5Y+K6YCJ5oup6buY6K6kYWVz44CCDQoNCmBgYHtyIGluaXRpYWwgb2YgZ2dwbG90fQ0KcCA8LSBnZ3Bsb3QoZGF0YSA9ICwgYWVzKHggPSAsIHkgPSApKQ0KYGBgDQoNCmRhdGHlsLHmmK/ovb3lhaXkvaDopoHnlLvnmoTmlbDmja7miYDlnKjnmoTmlbDmja7moYbvvIzmjIflrprkuLrkvaDnmoTnu5jlm77njq/looPvvIzovb3lhaXkuYvlkI7vvIzlsLHlj6/ku6XlhY3ljrvlhpnlpKfph4/nmoQk5p2l5o+Q5Y+WZGF0YS5mcmFtZeS5i+S4reeahOWQkemHj+OAgg0KDQrlvZPnhLbvvIzlpoLmnpzkvaDnmoTmlbDmja7pg73mmK/lkJHph4/vvIzkuZ/lj6/kuI3mjIflrprvvIzkvYbmmK/opoHlnKjnlLPmmI7kuK3moIfms6hkYXRhID0gTlVMTO+8jOS4jeeEtuWwseS8muW+l+WIsOS4jeW/heimgeeahOaKpemUmeOAgg0KDQoNCktleSBQb2ludDogDQoNCj4gMS4gZ2dwbG90MueahOaguOW/g+eQhuW/teaYr+Wwhue7mOWbvuS4juaVsOaNruWIhuemu++8jOaVsOaNruebuOWFs+eahOe7mOWbvuS4juaVsOaNruaXoOWFs+eahOe7mOWbvuWIhuemuw0KDQo+IDIuIGdncGxvdDLmmK/mjInlm77lsYLkvZzlm74NCg0KPiAzLiBnZ3Bsb3Qy5L+d5pyJ5ZG95Luk5byP5L2c5Zu+55qE6LCD5pW05Ye95pWw77yM5L2/5YW25pu05YW354G15rS75oCnDQoNCj4gNC4gZ2dwbG90MuWwhuW4uOingeeahOe7n+iuoeWPmOaNouiejeWFpeWIsOS6hue7mOWbvuS4reOAgg0KDQo+IFtob3dfdG9fdXNlX2dnZ3Bsb3QyXShodHRwOi8vd3d3LmNlbGx5c2UuY29tL2hvd190b191c2VfZ2dncGxvdDJfcGFydDEvKQ0KDQrlsL3nrqFxcGxvdOS9nOS4umdncGxvdDLnmoTlv6vpgJ/kvZzlm74ocXVpY2sgcGxvdCnlh73mlbAsIOiDveWkn+aegeWkp+eahOeugOWMluS9nOWbvuatpemqpCwg5a655piT5YWl6Zeo5ZKM5LiK5omLLCDkvYbmmK9xcGxvdOWNtOS4jeaYr+azm+Wei+WHveaVsCwg6ICMZ2dwbG90KCnkvZzkuLrms5vlnovlh73mlbAsIOiDveWvueS7u+aEj+exu+Wei+eahFLlr7nosaHov5vooYzlj6/op4bljJbmk43kvZwsIOaYr2dncGxvdDLnmoTnsr7pq5PmiYDlnKgsIOWboOiAjOWcqOacrOaWh+S4reS4u+imgeeahOe7mOWbvumDveaYr+mAmui/h2dncGxvdCgp5p2l5a6M5oiQ55qE44CC5pyJ5YWz5LqOcXBsb3TnmoTku4vnu43lj6/ku6Xnu4bnnItIYWRsZXnnmoTlrpjmlrnku4vnu43jgIINCg0K5ZyoSGFkbGV555qEZ2dwbG90MuWumOaWueaWh+aho+S4rSwgSGFkZWx56L+Z5qC35a+5V2lsa2luc29u55qE5Zu+5b2i6K+t5rOV6L+b6KGM5LqG5o+P6L+w77yaDQoNCj4g4oCc5LiA5byg57uf6K6h5Zu+5b2i5bCx5piv5LuO5pWw5o2u5Yiw5Yeg5L2V5a+56LGhKGdlb21ldHJpYyBvYmplY3QsIOe8qeWGmeS4umdlb20sIOWMheaLrOeCueOAgee6v+OAgeadoeW9ouetiSnnmoTlm77lvaLlsZ7mgKcoYWVzdGhldGljIGF0dHJpYnV0ZXMsIOe8qeWGmeS4umFlcywg5YyF5ous6aKc6Imy44CB5b2i54q244CB5aSn5bCP562JKeeahOS4gOS4quaYoOWwhOOAguatpOWkliwg5Zu+5b2i5Lit6L+Y5Y+v6IO95YyF5ZCr5pWw5o2u55qE57uf6K6h5Y+Y5o2iKHN0YXRpc3RpY2FsIHRyYW5zZm9ybWF0aW9uLCDnvKnlhpnkuLpzdGF0cyksIOacgOWQjue7mOWItuWcqOafkOS4queJueWumueahOWdkOagh+ezuyhjb29yZGluYXRlIHN5c3RlbSwgIOe8qeWGmeS4umNvb3JkKeS4rSwg6ICM5YiG6Z2iKGZhY2V0LCDmjIflsIbnu5jlm77nqpflj6PliJLliIbkuLroi6XlubLkuKrlrZDnqpflj6Mp5YiZ5Y+v5Lul55So5p2l55Sf5oiQ5pWw5o2u5Lit5LiN5ZCM5a2Q6ZuG55qE5Zu+5b2i44CC4oCdDQoNCuWboOatpOWcqGdncGxvdDLkuK0sIOWbvuW9ouivreazleS4reiHs+WwkeWMheaLrOS6huWmguS4izfkuKrlm77lvaLpg6jku7bku6Xlj4rkuKTkuKrmlYjnjofnu4Tku7bvvIjkuLvpopjvvIzlrZjlgqgr6L6T5Ye677yJ77yaDQoNCiMjIyAxLiDmlbDmja4oZGF0YSkNCg0K5pWw5o2uKGRhdGEp5YyF5ZCr5LqG5Y+Y6YePdmFyaWFibGXkuI7mlbDlgLwNCg0KMS4g5ZyoZ2dwbG90MuS4rSwg5omA5o6l5Y+X55qE5pWw5o2u6ZuG5b+F6aG75Li65pWw5o2u5qGGKGRhdGEuZnJhbWUp5qC85byPLCDlpoLlhoXnva7nmoRtdGNhcnPmlbDmja7pm4bvvJoNCmBgYHtyIG10Y2FycyBkYXRhc2V0fQ0KaGVhZChtdGNhcnMpDQojICAgICAgICAgICAgICAgICAgICBtcGcgY3lsIGRpc3AgIGhwIGRyYXQgICAgd3QgIHFzZWMgdnMgYW0gZ2VhciBjYXJiDQojIE1hemRhIFJYNCAgICAgICAgIDIxLjAgICA2ICAxNjAgMTEwIDMuOTAgMi42MjAgMTYuNDYgIDAgIDEgICAgNCAgICA0DQojIE1hemRhIFJYNCBXYWcgICAgIDIxLjAgICA2ICAxNjAgMTEwIDMuOTAgMi44NzUgMTcuMDIgIDAgIDEgICAgNCAgICA0DQojIERhdHN1biA3MTAgICAgICAgIDIyLjggICA0ICAxMDggIDkzIDMuODUgMi4zMjAgMTguNjEgIDEgIDEgICAgNCAgICAxDQojIEhvcm5ldCA0IERyaXZlICAgIDIxLjQgICA2ICAyNTggMTEwIDMuMDggMy4yMTUgMTkuNDQgIDEgIDAgICAgMyAgICAxDQojIEhvcm5ldCBTcG9ydGFib3V0IDE4LjcgICA4ICAzNjAgMTc1IDMuMTUgMy40NDAgMTcuMDIgIDAgIDAgICAgMyAgICAyDQojIFZhbGlhbnQgICAgICAgICAgIDE4LjEgICA2ICAyMjUgMTA1IDIuNzYgMy40NjAgMjAuMjIgIDEgIDAgICAgMyAgICAxDQpgYGANCg0KMi4g6L+Z56eN5qC85byP5bim5p2l55qE5aW95aSE5piv5pWw5o2u5piT5LqO5a2Y5YKoLCDkuZ/og73lnKjkv53nlZnljp/mnInnmoTnu5jlm77lj4LmlbDkuIssIOeUqCUrJeaWueS+v+WcsOWPmOabtOW3suacieaVsOaNrumbhuOAgg0KDQrkuLrpgJrov4figJ0r4oCd5Lul5Zu+5bGC55qE5pa55byP5Yqg5YWl54K555qE5Yeg5L2V5a+56LGhIA0KDQpgYGB7ciB1c2UgcGx1cyB0byBhZGQgbGF5ZXJ9DQpwIDwtIGdncGxvdChtdGNhcnMsIGFlcyhtcGcsIHd0LCBjb2xvdXIgPSBjeWwpKSArDQogIGdlb21fcG9pbnQoKSAjZ2VvbV9wb2ludCgp5Li66YCa6L+H4oCdK+KAneS7peWbvuWxgueahOaWueW8j+WKoOWFpeeCueeahOWHoOS9leWvueixoQ0KcA0KDQojIERhdGEgdHJhbnNmb3JtDQptdGNhcnMuYyA8LSB0cmFuc2Zvcm0obXRjYXJzLCBtcGcgPSBtcGdeMikNCg0KI+eUqG10Y2Fycy5j5pu/5o2ibXRjYXJzDQpwICUrJSBtdGNhcnMuYyANCmBgYA0KDQozLiDogIxnZ3Bsb3Qy6L+b6KGM5pWw5o2u5YiG57uE5pe25b+F6aG75qC55o2u6KGMLCDogIzkuI3og73moLnmja7liJcsIOS+i+WmguWcqG10Y2Fyc+eahOaVsOaNrumbhuS4rSwg5Y+v5Lul5oqK5rG96L2m5oyJ5rG957y45pWw6L+b6KGM5YiG57uELCDkvYbkuI3og73mjInmsb3ovabnmoTmoaPkvY3mlbDlkozmsb3nvLjmlbDov5nkuKTkuKrlj5jph4/liIbkuLrkuKTnu4TjgILov5nopoHmsYLmiorigJzlrr3igJ3mlbDmja7ovazljJbkuLrigJzplb/igJ3mlbDmja7jgILmiYDosJPnmoTplb/mlbDmja7mmK/lj5jph4/kuI3lho3mmK/mlL7lnKjlkITkuKrliJfkuIosIOiAjOaYr+aOkuaIkOS4gOWIlywg5q+P5LiA5Liq5Y+Y6YeP6YO95YiG5Yir5Y2g5YW25Lit55qE5Yeg6KGMLCDov5nmoLflsLHog73mlrnkvr/nmoTlr7nmr4/kuKrlj5jph4/ov5vooYzliIbnu4TjgIJyZXNoYXBlMuS4rW1lbHQoKeWSjGNhc3QoKeiDveWkn+eBtea0u+eahOiejeWQiChtZWx0KeWSjOmHjemTuChjYXN0KeWcqOaVsOaNruahhuS4reeahOaVsOaNruOAgg0KDQpgYGB7ciByZXNoYXAyIG1lbHQgZGF0YX0NCg0KIyBMb2FkIHJlc2hhcGUyIHBhY2thZ2UNCmlmKCFzdXBwcmVzc1dhcm5pbmdzKHJlcXVpcmUocmVzaGFwZTIpKSkNCnsNCiAgICBpbnN0YWxsLnBhY2thZ2VzKCdyZXNoYXBlMicpDQogICAgcmVxdWlyZShyZXNoYXBlMikNCn0NCg0KIyDono3lkIjmoaPkvY3mlbBnZWFy5ZKM5rG957y45pWwY3ls6L+Z5Lik5Liq5Y+Y6YeP5Yiw5LiA5YiX77yM5a+55bqU55qE5YC85Yiw5Y+m5LiA5YiXDQojIG1wZyBjeWwgZGlzcCAgaHAgZHJhdCAgICB3dCAgcXNlYyB2cyBhbSBnZWFyIGNhcmINCm10Y2Fycy5tIDwtIG1lbHQobXRjYXJzLCBpZCA9IGMoIm1wZyIsICJkaXNwIiwgImhwIiwgImRyYXQiLCAid3QiLCAicXNlYyIsICJ2cyIsICJjYXJiIikpIA0KDQojaGVhZChtdGNhcnMubSwyMCkNCmhlYWQobXRjYXJzKQ0KaGVhZChtdGNhcnMubSkNCmBgYA0KDQoNCiMjIyAyLiDmmKDlsIQobWFwcGluZykNCg0KIyMjIyAoMSkg5pig5bCE55qE5qaC5b+1DQoNCmBhZXMoKWDlh73mlbDmmK9nZ3Bsb3Qy5Lit55qE5pig5bCE5Ye95pWwLCDmiYDosJPnmoTmmKDlsITljbPkuLrmlbDmja7pm4bkuK3nmoTmlbDmja7lgLzlhbPogZTliLDnm7jlupTnmoTlm77lvaLlsZ7mgKcoYWVzdGhldGljKei/h+eoi+S4reS4gOenjeWvueW6lOWFs+ezuywg5Zu+5b2i5bGe5oCn77yIYWVzdGhldGljKeWMheaLrO+8muaoque6teWdkOagh+OAgeeCueeahOWkp+Wwj+OAgeminOiJsuOAgeW9oueKtuOAgeWhq+WFheiJsuetieOAgmF0dHJpYnV0ZXPvvIznroDnp7BhZXM/PyDkvovlpoLvvJoNCmBgYHtyIGdncGxvdDIgYWVzKCl9DQpwMSA8LSBnZ3Bsb3QoZGF0YSA9IG10Y2FycykNCnN1bW1hcnkocDEpDQojIGRhdGE6IG1wZywgY3lsLCBkaXNwLCBocCwgZHJhdCwgd3QsIHFzZWMsIHZzLA0KIyAgIGFtLCBnZWFyLCBjYXJiIFszMngxMV0NCiMgZmFjZXRpbmc6IDxnZ3Byb3RvIG9iamVjdDogQ2xhc3MgRmFjZXROdWxsLCBGYWNldD4NCiMgICAgIGNvbXB1dGVfbGF5b3V0OiBmdW5jdGlvbg0KIyAgICAgZHJhd19iYWNrOiBmdW5jdGlvbg0KIyAgICAgZHJhd19mcm9udDogZnVuY3Rpb24NCiMgICAgIGRyYXdfbGFiZWxzOiBmdW5jdGlvbg0KIyAgICAgZHJhd19wYW5lbHM6IGZ1bmN0aW9uDQojICAgICBmaW5pc2hfZGF0YTogZnVuY3Rpb24NCiMgICAgIGluaXRfc2NhbGVzOiBmdW5jdGlvbg0KIyAgICAgbWFwOiBmdW5jdGlvbg0KIyAgICAgbWFwX2RhdGE6IGZ1bmN0aW9uDQojICAgICBwYXJhbXM6IGxpc3QNCiMgICAgIHJlbmRlcl9iYWNrOiBmdW5jdGlvbg0KIyAgICAgcmVuZGVyX2Zyb250OiBmdW5jdGlvbg0KIyAgICAgcmVuZGVyX3BhbmVsczogZnVuY3Rpb24NCiMgICAgIHNldHVwX2RhdGE6IGZ1bmN0aW9uDQojICAgICBzZXR1cF9wYXJhbXM6IGZ1bmN0aW9uDQojICAgICBzaHJpbms6IFRSVUUNCiMgICAgIHRyYWluOiBmdW5jdGlvbg0KIyAgICAgdHJhaW5fcG9zaXRpb25zOiBmdW5jdGlvbg0KIyAgICAgdHJhaW5fc2NhbGVzOiBmdW5jdGlvbg0KIyAgICAgdmFyczogZnVuY3Rpb24NCiMgICAgIHN1cGVyOiAgPGdncHJvdG8gb2JqZWN0OiBDbGFzcyBGYWNldE51bGwsIEZhY2V0Pg0KDQpwMiA8LSBnZ3Bsb3QoZGF0YSA9IG10Y2FycywgbWFwcGluZyA9IGFlcyh4ID0gd3QsIHkgPSBocCwgY29sb3IgPSBnZWFyKSkNCnN1bW1hcnkocDIpDQojIGRhdGE6IG1wZywgY3lsLCBkaXNwLCBocCwgZHJhdCwgd3QsIHFzZWMsIHZzLA0KIyAgIGFtLCBnZWFyLCBjYXJiIFszMngxMV0NCiMgbWFwcGluZzogIHggPSB3dCwgeSA9IGhwLCBjb2xvdXIgPSBnZWFyDQojIGZhY2V0aW5nOiA8Z2dwcm90byBvYmplY3Q6IENsYXNzIEZhY2V0TnVsbCwgRmFjZXQ+DQojICAgICBjb21wdXRlX2xheW91dDogZnVuY3Rpb24NCiMgICAgIGRyYXdfYmFjazogZnVuY3Rpb24NCiMgICAgIGRyYXdfZnJvbnQ6IGZ1bmN0aW9uDQojICAgICBkcmF3X2xhYmVsczogZnVuY3Rpb24NCiMgICAgIGRyYXdfcGFuZWxzOiBmdW5jdGlvbg0KIyAgICAgZmluaXNoX2RhdGE6IGZ1bmN0aW9uDQojICAgICBpbml0X3NjYWxlczogZnVuY3Rpb24NCiMgICAgIG1hcDogZnVuY3Rpb24NCiMgICAgIG1hcF9kYXRhOiBmdW5jdGlvbg0KIyAgICAgcGFyYW1zOiBsaXN0DQojICAgICByZW5kZXJfYmFjazogZnVuY3Rpb24NCiMgICAgIHJlbmRlcl9mcm9udDogZnVuY3Rpb24NCiMgICAgIHJlbmRlcl9wYW5lbHM6IGZ1bmN0aW9uDQojICAgICBzZXR1cF9kYXRhOiBmdW5jdGlvbg0KIyAgICAgc2V0dXBfcGFyYW1zOiBmdW5jdGlvbg0KIyAgICAgc2hyaW5rOiBUUlVFDQojICAgICB0cmFpbjogZnVuY3Rpb24NCiMgICAgIHRyYWluX3Bvc2l0aW9uczogZnVuY3Rpb24NCiMgICAgIHRyYWluX3NjYWxlczogZnVuY3Rpb24NCiMgICAgIHZhcnM6IGZ1bmN0aW9uDQojICAgICBzdXBlcjogIDxnZ3Byb3RvIG9iamVjdDogQ2xhc3MgRmFjZXROdWxsLCBGYWNldD4NCmBgYA0K5Y+v5Lul5Y+R546wLCDlnKhwMuS4rSwg6YCa6L+HYWVzKCnmjIflrprkuobmqKrnurXlnZDmoIfliIbliKvkuLp3dOWSjGhwLCDpopzoibLkuLpnZWFy6L+Z5LiJ56eN5Zu+5b2i5bGe5oCnLOWcqG1hcHBpbmfph4zovrnkuobjgIIg5ZyoZ2dwbG90MuS4reS4jeWQjOeahOWHoOS9leWvueixoeWvueW6lOedgOS4jeWQjOeahOWbvuW9ouWxnuaApywg5pyJ5YWz5LqO5Yeg5L2V5a+56LGh55qE5bCG5Zyo5LiL6Z2i55qE5bCP6IqC6K6y6Kej44CCDQoNCuS7u+S9leS4juaVsOaNruWQkemHj+mhuuW6j+ebuOWFs++8jOmcgOimgemAkOS4quaMh+WumueahOWPguaVsOmDveW/hemhu+WGmeWcqGFlc+mHjOOAgg0KDQoNCiMjIyMgKDIpIOiuvuWumuWSjOaYoOWwhA0KDQrmmKDlsITmmK/lsIbkuIDkuKrlj5jph4/kuK3nprvmlaPmiJbov57nu63nmoTmlbDmja7kuI7kuIDkuKrlm77lvaLlsZ7mgKfkuK3ku6XkuI3lkIznmoTlj4LmlbDmnaXnm7jkupLlhbPogZQsIOiAjOiuvuWumuiDveWkn+Wwhui/meS4quWPmOmHj+S4reaJgOacieeahOaVsOaNrue7n+S4gOS4uuS4gOS4quWbvuW9ouWxnuaAp+OAgg0KDQpgYGB7ciBhZXN0aGV0aWMgYW5kIG1hcHBpbmd9DQpwIDwtIGdncGxvdChtdGNhcnMsIGFlcyh3dCwgbXBnKSkNCg0KI+iuvuWumuaVo+eCueeahOminOiJsuS4uuiTneiJsg0KcCArIGdlb21fcG9pbnQoY29sb3IgPSAiYmx1ZSIpIA0KDQojIOS4i+mdoueahOacgOWQjuS4gOihjOivreWPpeS4uumUmeivr+eahOaYoOWwhOWFs+ezuywgDQojIOWcqGFlc+S4rSwgY29sb3IgPSDigJxibHVl4oCd55qE5a6e6ZmF5oSP5oCd5piv5oqK4oCdYmx1ZeKAneW9k+S4uuS4gOS4quWPmOmHjywgDQojIOeUqOi/meS4quWPmOmHj+mHjOeahOaVsOaNruWOu+WFs+iBlOWbvuW9ouWxnuaAp+S4reeahOWPguaVsCwNCiMg5Zug5Li64oCdYmx1ZeKAneWPquWQq+acieS4gOS4quWtl+espuWPmOmHjywg6buY6K6k5oOF5Ya15LiL5Li656a75pWj5Y+Y6YePLA0KIyDmjInpu5jorqTnmoTpopzoibLmoIfluqbmoIforrDkuLrmoYPnuqLoibINCnAgKyBnZW9tX3BvaW50KGFlcyhjb2xvciA9ICJibHVlIikpDQpgYGANCg0KDQojIyMjICgzKSDliIbnu4QoZ3JvdXApDQoNCuWIhue7hChncm91cCnkuZ/mmK9nZ3Bsb3Qy56eN5pig5bCE5YWz57O755qE5LiA56eNLCDpu5jorqTmg4XlhrXkuItnZ3Bsb3Qy5oqK5omA5pyJ6KeC5rWL54K55YiG5Li65LqG5LiA57uELCDlpoLmnpzpnIDopoHmiorop4LmtYvngrnmjInpop3lpJbnmoTnprvmlaPlj5jph4/ov5vooYzliIbnu4TlpITnkIYsIOW/hemhu+S/ruaUuem7mOiupOeahOWIhue7hOiuvue9ruOAgg0KDQpgYGB7ciBncm91cH0NCnAxIDwtIGdncGxvdChkYXRhID0gbXRjYXJzLCBtYXBwaW5nID0gYWVzKHggPSB3dCwgeSA9IGhwKSkgKyBnZW9tX2xpbmUoKSANCiPpu5jorqTliIbnu4Torr7nva4sIOWNs2dyb3VwPTENCiNnZW9tX2xpbmXkuLrmipjnur/lm77nmoTlh6DkvZXlr7nosaENCnAyIDwtIGdncGxvdChkYXRhID0gbXRjYXJzLCBtYXBwaW5nID0gYWVzKHggPSB3dCwgeSA9IGhwLCBncm91cCA9IGZhY3RvcihnZWFyKSkpICsgZ2VvbV9saW5lKCkgDQoj5oqKd3TlkoxocOaJgOWvueW6lOeahOingua1i+eCueaMiWdlYXIoZ2VhcuS7peWboOWtkOWMluWPmOS4uuemu+aVo+WPmOmHjynov5vooYzliIbnu4QNCg0KYGBgDQojIyMjICg0KSDlm77lsYIobGF5ZXIpDQoNCui/meS6m+e7hOS7tuS5i+mXtOaYr+mAmui/h+KAnCvigJ0sIOS7peWbvuWxgihsYXllcinnmoTmlrnlvI/mnaXnspjlkIjmnoTlm77nmoQsIOaJgOS7peWbvuWxguaYr2dncGxvdDLkuK3kuIDkuKrph43opoHnmoTmpoLlv7XjgIINCg0K5Zyo5LiK5paH5Lit6YCa6L+H5a+55pWw5o2u5ZKM5pig5bCE55qE6K6y6Kej5LitLCDmiJHku6zlt7Lnu4/ph4fnlKjov4figJ0r4oCd5p2l5re75Yqg5Zu+5bGCLCBnZ3Bsb3Qy5Lit5Zu+5bGC55qE5qaC5b+15ZKMUFPkuK3lm77lsYLnmoTmpoLlv7Xlvojlg48sIOWPr+S7pei/meagt+eQhuino2dncGxvdDLkuK3nmoTlm77lsYLvvJrmr4/kuKrlm77lsYLlj6/ku6Xku6PooajkuIDkuKrlm77lvaLnu4Tku7YsIOS+i+WmguS4i+mdouimgeS7i+e7jeeahOWHoOS9leWvueixoeOAgee7n+iuoeWPmOaNouetieWbvuW9oue7hOS7tiwg6L+Z5Lqb57uE5Lu25Lul5Zu+5bGC55qE5pa55byP5Y+g5Yqg5Zyo5LiA6LW35p6E5oiQ5LiA5Liq57uY5Zu+55qE5pW05L2TLCDlnKjmr4/kuKrlm77lsYLkuK3nmoTlm77lvaLnu4Tku7blj4jlj6/ku6XliIbliKvorr7lrprmlbDmja7jgIHmmKDlsITmiJblhbbku5bnm7jlhbPlj4LmlbAsIOWboOatpOe7hOS7tuS5i+mXtOWPiOaYr+WFt+acieebuOWvueeLrOeri+aAp+eahOOAgmdncGxvdDLkuK3lm77lsYLnmoTorr7lrprmmK/ljYHliIbmiJDlip/nmoQsIOWboOS4uui/meS4gOi/h+eoi+aYr+WmguatpOWunueUqOOAgeaWueS+v+iAjOWvjOaciemAu+i+keaAp+OAgg0KDQojIyMjIyAoMSkg5Zyo5Yeg5L2V5a+56LGh5Lit6K6+5a6a5pig5bCEDQoNCuWJjemdouaIkeS7rOW3suWcqGdncGxvdCgp5Lit6K6+5a6a5LqG5pig5bCE5LqG5YWz57O7LCDov5nnp43mmKDlsITlhbPns7vmmK/pu5jorqTnmoQsIOaIkeS7rOWPr+S7peWcqOWQjumdoueahOWHoOS9leWvueixoeS4reayv+eUqOW3suiuvuWumueahOm7mOiupOaYoOWwhOWFs+ezuywg5Lmf5Y+v5Lul6ZqP5pe25Zyo5Yeg5L2V5a+56LGh5Lit6L+b6KGM5pu05pS544CCDQpgYGB7ciBhZXMgYWRkL21vZGlmeX0NCnAgPC0gZ2dwbG90KG10Y2FycywgYWVzKHggPSBtcGcsIHkgPSB3dCwgY29sb3IgPSBmYWN0b3IoZ2VhcikpKQ0KI+iuvuWumum7mOiupOeahOaYoOWwhOWFs+ezuw0KDQpwICsgZ2VvbV9wb2ludCgpDQoj5rK/55So6buY6K6k55qE5pig5bCE5YWz57O75p2l57uY5Yi25pWj54K55Zu+DQoNCnAgKyBnZW9tX3BvaW50KGFlcyhzaGFwZSA9IGZhY3RvcihjYXJiKSkpIA0KI+a3u+WKoOWbvuWxguS4reeahHNoYXBl55qE5pig5bCE5YWz57O7DQoNCnAgKyBnZW9tX3BvaW50KGFlcyh5ID0gY2FyYikpDQoj5L+u5pS56buY6K6k55qEeeeahOaYoOWwhOWFs+ezuywg5rOo5oSP5Zu+5Liteei9tOWQjeensOS7jeeEtuS7pem7mOiupOeahHd06KGo56S6DQoNCnAgKyBnZW9tX3BvaW50KGFlcyhjb2xvciA9IE5VTEwpKQ0KICPliKDpmaTpu5jorqTnmoRjb2xvcuaYoOWwhOWFs+ezuw0KYGBgDQrlsL3nrqHkuIrpnaLkuInkuKrmnInlhbPkvovlrZDlnKjlrp7pmYXlupTnlKjkuK3lvojlsJHljrvmtonlj4osIOS9huaYr+W+iOWlveeahOivtOaYjuS6huWbvuWxguOAgeaVsOaNruWSjOaYoOWwhOS5i+mXtOeahOWFs+ezu+OAgg0KDQojIyMjIyAoMikg6YeH55So5aSa5Liq5pWw5o2u6ZuG5oiW5ZCR6YeP5pWw5o2u57uY5Zu+DQrlnKjlvojlpJrnp43nu5jlm77lnLrlkIjkuK0sIOaIkeS7rOS8mui/kOeUqOWIsOWkmuS4quaVsOaNrumbhuaIluWQkemHj+aVsOaNruadpei/m+ihjOWbvuWxguWPoOWKoCwg5YW35L2T55qE5L6L5a2Q5aaC5LiLDQpgYGB7ciB1c2UgZGlmZmVyZW50IGRhdGFzZXR9DQoj5p6E5bu65LiN5ZCM5LqObXRjYXJz55qE5pWw5o2u6ZuGbXRjYXJzLmMNCm10Y2Fycy5jIDwtIHRyYW5zZm9ybShtdGNhcnMsIG1wZyA9IG1wZ14yKQ0KDQpnZ3Bsb3QoKSsNCiAgZ2VvbV9wb2ludChhZXMoeCA9IGhwLCB5ID0gbXBnKSwgZGF0YSA9IG10Y2FycywgY29sb3IgPSAicmVkIikgKyANCiAgZ2VvbV9wb2ludChhZXMoeCA9IG10Y2FycyRocCwgeSA9IG10Y2FycyRkaXNwKSwgY29sb3IgPSAiZ3JlZW4iKSsgDQogICPpgInnlKjlkJHph4/mlbDmja4NCiAgZ2VvbV9wb2ludChhZXMoeCA9IGhwLCB5PSBtcGcpLCBkYXRhID0gbXRjYXJzLmMsIGNvbG9yID0gImJsdWUiKSANCiAgI+mAieeUqOS4jeWQjOeahOaVsOaNrumbhg0KYGBgDQoNCiMjIyMjICgzKSDliqDms6jph4oNCg0K5omA5pyJ5rOo6YeK55qE5a6e546w6YO95piv6YCa6L+HYW5ub3RhdGXlh73mlbDlrp7njrDnmoTvvIzlhbblrp5hbm5vdGF0ZeWwseaYr+S4gOS4quacgOeugOWNleeahGdlb21f5Y2V5YWD77yM5a6D5LiA5qyh5Y+q5re75Yqg5LiA5Liq5L2N572u5LiK55qE5Zu+5b2i77yI5Y+v5Lul6YCa6L+H6K6+572u5ZCR6YeP5p2l5a6e546w5ZCM5pe257uY5Yi25aSa5Liq5Zu+5b2i77yM5L2G6L+Z5Liq55CG5b+15ZKM5rOo6YeK55qE55CG5b+15pyJ5omA5YGP5beu77yJ44CCYW5ub3RhdGXnmoRnZW9t5bCx5piv5oyH5a6a5rOo6YeK55qE57G75Z6L77yM5YW25bGe5oCn5oyJ54WnZ2VvbeeahOS4jeWQjOiAjOWPkeeUn+WPmOWMluOAgg0KDQoNCiMjIyAzLiDlh6DkvZXlr7nosaEoZ2VvbSkNCg0K5LiK6Z2i5oyH5a6a55qE5Zu+5b2i5bGe5oCn6ZyA6KaB5ZGI546w5Zyo5LiA5a6a55qE5Yeg5L2V5a+56LGh5LiK5omN6IO96KKr5oiR5Lus55yL5Yiw77yM6L+Z5Lqb5om/6L295Zu+5b2i5bGe5oCn55qE5a+56LGh5Y+v6IO95piv54K577yM5Y+v6IO95piv57q/77yM5Y+v6IO95pivYmFy44CC5Yeg5L2V5a+56LGh5omn6KGM552A5Zu+5bGC55qE5a6e6ZmF5riy5p+TLCDmjqfliLbnnYDnlJ/miJDnmoTlm77lg4/nsbvlnovjgIINCg0K5L6L5aaC55SoZ2VvbV9wb2ludCgp5bCG5Lya55Sf5oiQ5pWj54K55Zu+LCDogIxnZW9tX2xpbmXkvJrnlJ/miJDmipjnur/lm77jgIINCg0K5pyA5bi46KeB55qE5Zu+5pyJ5pWj54K55Zu+44CB5oqY57q/5Zu+44CB5puy57q/5Zu+44CB5p2h5b2i5Zu+44CB55u05pa55Zu+77yM5YiG5Yir5a+55bqUZ2VvbV9wb2ludCgpLCBnZW9tX2xpbmUoKSwgZ2VvbV9zbW9vdGgoKSwgZ2VvbV9iYXIoKSwgZ2VvbV9oaXN0b2dyYW0oKeOAguebuOW6lOeahGFlc+S4reeahOWPmOmHj+S4jeS4gOWumuaYr3jlkox577yM5a+55LqO5p2h5b2i5Zu+5ZKM55u05pa55Zu+5bCx5Y+q5pyJ5LiA5Liq5Y+Y6YePeOOAgg0KDQoNCj4g5q+P5LiA5Liq5Yeg5L2V5a+56LGh6YO95pyJ5LiA5Liq6buY6K6k55qE57uf6K6h5Y+Y5o2iLCDlubbkuJTmr4/kuIDkuKrnu5/orqHlj5jmjaLpg73mnInkuIDkuKrpu5jorqTnmoTlh6DkvZXlr7nosaHjgILmraPlm6DlpoLmraQsIOi/meS4gOiuvuWumuWwhuS8muS9v+e7mOWbvui/h+eoi+WPmOeahOeBtea0u+WkmuWPmOOAgg0KDQrlnKhnZ3Bsb3Qy55qE5a6Y5pa557Si5byV5LitLCDlt7Llr7lnZ3Bsb3Qy5Lit5omA5pyJ55qEZ2VvbeWSjHN0YXTnu4Tku7bov5vooYzkuobmsYfmgLssIOabtOivpue7hueahOWGheWuuSwg5Y+v55u05o6l54K55byA55u45bqU5Zu+5b2i57uE5Lu25omA5a+55bqU55qE6ZO+5o6l44CC5LiL6Z2i5a+55Yeg5Liq5bi455So55qE5Yeg5L2V5a+56LGh5ZKM57uf6K6h5Y+Y5o2i6L+b6KGM5Li+5L6L5o+P6L+w44CCDQoNCiMjIyMgKDEpIGdlb21fcG9pbnQoKeaVo+eCueWbvg0KYGBge3IgZ2VvbV9wb2ludH0NCnAgPC0gZ2dwbG90KG10Y2FycywgYWVzKHd0LCBtcGcpKQ0KcCArIGdlb21fcG9pbnQoKQ0KDQoj5pu05pS56aKc6ImyLei/nue7reWPmOmHjw0KcCArIGdlb21fcG9pbnQoYWVzKGNvbG9yID0gcXNlYykpDQojIHAgKyBnZW9tX3BvaW50KGFlcyhjb2xvciA9IGZhY3Rvcihxc2VjKSkpDQoNCiPmm7TmlLnpopzoibIt56a75pWj5Y+Y6YePDQpwICsgZ2VvbV9wb2ludChhZXMoY29sb3IgPSBmYWN0b3IoZ2VhcikpKQ0KDQoj5pu05pS56YCP5piO5bqmDQpwICsgZ2VvbV9wb2ludChhZXMoYWxwaGEgPSBxc2VjKSkNCg0KI+abtOaUueW9oueKtg0KcCArIGdlb21fcG9pbnQoYWVzKHNoYXBlID0gZmFjdG9yKGdlYXIpKSkNCg0KI+abtOaUueeCueWkp+Wwjw0KcCArIGdlb21fcG9pbnQoYWVzKHNpemUgPSBxc2VjKSkNCg0KDQoj5Lik56eN6aKc6Imy55qE5Y+g5YqgDQpwICsgZ2VvbV9wb2ludChjb2xvciA9ICJncmV5NTAiLCBzaXplID0gNSkgKyBnZW9tX3BvaW50KGFlcyhjb2xvciA9IHFzZWMpLCBzaXplID0gNCkNCg0KI+minOiJsuWSjOW9oueKtueahOWPoOWKoA0KcCArIGdlb21fcG9pbnQoY29sb3IgPSAiZ3JleTUwIiwgc2l6ZSA9IDUpICsgZ2VvbV9wb2ludChhZXMoc2hhcGUgPSBmYWN0b3IoZ2VhcikpLCBzaXplID0gMykNCg0KcCArIGdlb21fcG9pbnQoYWVzKHNpemUgPSBxc2VjLCBzaGFwZSA9IGZhY3RvcihnZWFyKSwgY29sb3IgPSBmYWN0b3IoZ2VhcikpKQ0KDQpgYGANCg0KIyMjIyAoMikgZ2VvbV9oaXN0b2dyYW0oKQ0KYGBge3IgZ2VvbV9oaXN0b2dyYW19DQpyYXRpbmcgPC0gcnVuaWYoMTAwMCxtaW4gPSAxLCBtYXggPSAxMCkNCmlkIDwtIDE6MTAwMA0KbW92aWVzIDwtIGRhdGEuZnJhbWUoaWQsIHJhdGluZykNCg0KbSA8LSBnZ3Bsb3QobW92aWVzLCBhZXMocmF0aW5nKSkgDQoj6L+Z6YeM5L2/55SobW92aWVz5pWw5o2u6ZuGDQoNCm0gKyBnZW9tX2hpc3RvZ3JhbSgpDQptICsgZ2VvbV9oaXN0b2dyYW0oYmlud2lkdGggPSAwLjUpDQoj6LCD5pW05YiG566xKGJpbinmlbDmja4NCm0gKyBnZW9tX2hpc3RvZ3JhbShiaW53aWR0aCA9IDEpDQptICsgZ2VvbV9oaXN0b2dyYW0oYmlud2lkdGggPSAyKQ0KYGBgDQoNCmdlb21faGlzdG9ncmFtKCnov5nkuKrlh6DkvZXlr7nosaHpu5jorqTkvb/nlKhzdGF0X2Jpbui/meS4que7n+iuoeWPmOaNoiwg6ICM6L+Z5Liq57uf6K6h5Y+Y5o2i5Lya55Sf5oiQDQooMSljb3VudO+8muavj+S4que7hOmHjOingua1i+WAvOeahOaVsOebriwNCigyKWRlbnNpdHnvvJrmr4/kuKrnu4Tph4zop4LmtYvlgLznmoTlr4bluqblkowNCigzKXjvvJrnu4TnmoTkuK3lv4PkvY3nva4NCui/meS4ieS4quWPmOmHj+OAgg0K55Sf5oiQ55qE6L+Z5LiJ5Liq5Y+Y6YeP5ZyoZ2dwbG90KCnkuK3nmoTlho3kvb/nlKguLuWbtOi1t+adpSwg5Zug5q2k5Y+v5Lul55So5p2l55Sf5oiQ5aaC5LiL55qE5Zu+DQpgYGB7ciBzdGF0X2Jpbui/meS4que7n+iuoeWPmOaNon0NCm0gKyBnZW9tX2hpc3RvZ3JhbShiaW53aWR0aCA9IDAuNSwgYWVzKGZpbGwgPS4uY291bnQuLikpDQptICsgZ2VvbV9oaXN0b2dyYW0oYmlud2lkdGggPSAwLjUsIGFlcyh5ID0gLi5kZW5zaXR5Li4pKSArIGdlb21fZGVuc2l0eSgpDQpgYGANCg0K5p+x5b2i5Zu+5YWD57Sg5L2N572u55qE6LCD5pW0DQoNCuWFg+e0oOS9jee9rueahOiwg+aVtOWFseaciTXnp43vvJoNCigxKWRvZGdl77ya5bm25o6S5pa55byPOyANCigyKWZpbGzvvJrloIblj6Dlm77lg4/lhYPntKAsIOW5tuWwhumrmOW6puagh+WHhuWMluS4ujEsIA0KKDMpaWRlbnRpdHnvvJrkuI3lgZrku7vkvZXosIPmlbQ7DQooNClqaXR0ZXLvvJrnu5nngrnlop7liqDmibDliqjpgb/lhY3ph43lkIgsIA0Kaml0dGVy5L2/5p+Q5q+P5LiA5Liq54K55ZyoeOi9tOeahOaWueWQkeS4iuS6p+eUn+maj+acuueahOWBj+enuywg5LuO6ICM5YeP5bCR5LqG5Zu+5b2i6YeN5Y+g55qE6Zeu6aKYLCDlj6bkuIDnp43ku4vnu43ph43lj6DnmoTmlrnlvI/mmK/mlLnlj5jngrnnmoTpgI/mmI7luqYsIOWwhuWcqOWunuaImOS4reeahOWcsOWbvuiuqOiuuuOAgg0KKDUpc3RhY2vvvJrloIblj6Dlm77lg4/lhYPntKDjgIIgDQoNCmBgYHtyIHBvc2l0aW9ufQ0KZCA8LSBnZ3Bsb3QoZGlhbW9uZHMsIGFlcyh4ID0gY2xhcml0eSwgZmlsbCA9IGN1dCApKQ0KIyBkb2RnZQ0KZCArIGdlb21faGlzdG9ncmFtKHBvc2l0aW9uID0gImRvZGdlIiwgc3RhdCA9ICJjb3VudCIpDQoNCiMgZmlsbCBhbmQgc3RhbmRhcmQgdG8gMSBzdGFjaw0KZCArIGdlb21faGlzdG9ncmFtKHBvc2l0aW9uID0gImZpbGwiLCBzdGF0ID0gImNvdW50IikNCg0KIyBqdXN0IHN0YWNrDQpkICsgZ2VvbV9oaXN0b2dyYW0ocG9zaXRpb24gPSAic3RhY2siLCBzdGF0ID0gImNvdW50IikNCg0KIyBqaXR0ZXINCmdncGxvdChkaWFtb25kcykgKyBnZW9tX3BvaW50KGFlcyhjb2xvciwgcHJpY2UvY2FyYXQpLCBwb3NpdGlvbiA9ICJqaXR0ZXIiKQ0KYGBgDQrlvojlpJrmg4XlhrXkuIssIOaIkeS7rOS8mumHh+eUqOWbuuWumueahHjovbTlkox56L205YC85p2l6L+b6KGM5L2c5Zu+LCDmraTml7bpnIDopoHnlKhzdGF0ID0g4oCcaWRlbnRpdHnigJ0g5p2l55Sz5piOLCDljbPooajnpLrkuI3lr7nmlbDmja7ov5vooYznu5/orqHlj5jmjaINCmBgYHtyIGlkZW50aXR5fQ0KQSA8LSBjKDEsIDIsIDMsIDQsIDUsIDYsIDcsIDgpDQpCIDwtIGMoMiwgMTAsIDExLCA1LCA2LCAxLCAxMCwgMjApDQpnZ3Bsb3QoKSArIGdlb21faGlzdG9ncmFtKGFlcyh4ID0gQSwgeSA9IEIpLCBzdGF0ID0gImlkZW50aXR5IikNCmBgYA0KDQojIyMjICgzKSBnZW9tX3Ntb290aCgpDQoNCmdlb21fc21vb3RoKCnnlKjmnaXnu5nmlbDmja7mt7vliqDlubPmu5Hmm7Lnur8sIOaJgOiDvemHh+eUqOeahOaWueazleWMheaLrOS6hmxtLCBnbG0sIGdhbSwgbG9lc3MsIHJsbeetiSwg6L+Z5Lqb5pa55rOV6ZyA6KaB6YCa6L+H5Yqg6L295YWs5byP5p2l5a6e546w44CCDQpgYGB7ciBnZW9tX3Ntb290aH0NCm0gPC0gZ2dwbG90KG10Y2FycywgYWVzKHFzZWMsIHd0KSkNCiMgbQ0KDQojIOa3u+WKoOW5s+a7keabsue6vyArIOeCueWbvg0KbSArIHN0YXRfc21vb3RoKCkgKyBnZW9tX3BvaW50KCkNCg0KI+WPlua2iOW5s+a7keabsue6v+m7mOiupOeahOe9ruS/oeWMuumXtCArIOeCueWbvg0KbSArIHN0YXRfc21vb3RoKHNlID0gRkFMU0UpICsgZ2VvbV9wb2ludCgpDQoNCiPmm7TmlLnnva7kv6HljLrpl7Tlkoznur/mnaHpopzoibINCm0gKyBzdGF0X3Ntb290aChmaWxsID0gInJlZCIsIHNpemUgPSAyLCBhbHBoYSA9IDAuNSwgY29sb3IgPSAiZ3JlZW4iKSArIGdlb21fcG9pbnQoKQ0KDQoj55So5LiA5YWD5LiA5qyhKOe6v+aAp+WbnuW9kinmlrnnqIvmi5/lkIgNCm0gKyBzdGF0X3Ntb290aChtZXRob2QgPSAibG0iKSArIGdlb21fcG9pbnQoKSANCg0KI+S9v+eUqOS4gOWFg+S6jOasoeaWueeoi+aLn+WQiA0KbSArIHN0YXRfc21vb3RoKG1ldGhvZCA9ICJsbSIsIGZvcm11bGEgPSB5IH4gcG9seSh4LCAzKSkgKyBnZW9tX3BvaW50KCkgDQoNCg0KIyDliqDovb1zcGxpbmVz5ZKMTUFTU+WMhSwg5L2/55So6Ieq55Sx5bqm5Li6M+eahOiHqueEtuagt+adoeadpei/m+ihjOaLn+WQiA0KcmVxdWlyZShzcGxpbmVzKQ0KcmVxdWlyZShNQVNTKQ0KbSArIHN0YXRfc21vb3RoKG1ldGhvZCA9ICJsbSIsIGZvcm11bGEgPSB5IH4gbnMoeCwgMykpICsgZ2VvbV9wb2ludCgpDQoNCm0gPC0gZ2dwbG90KG10Y2FycywgYWVzKHkgPSB3dCwgeCA9IG1wZywgZ3JvdXAgPSBmYWN0b3IoY3lsKSkpDQoNCiPmjIljeWzov5nkuKrnprvmlaPlj5jph4/ov5vooYzliIbnu4QsIOWIhuWIq+aLn+WQiOaVsOaNrg0KbSArIHN0YXRfc21vb3RoKG1ldGhvZCA9IGxtLCBhZXMoY29sb3IgPSBmYWN0b3IoY3lsKSwgZmlsbCA9IGZhY3RvcihjeWwpKSkgKyBnZW9tX3BvaW50KCBhZXMoY29sb3IgPSBmYWN0b3IoY3lsKSkpDQoNCmBgYA0KDQrlvpfnm4rkuo5y5YyF55qE5Liw5a+M5oCnLCDmiJHku6zlj6/ku6Xph4fnlKjmnoHlpKfkvLznhLbmiJbmnIDlsI/kuozkuZjms5UsIOWvueWkmuenjeeOsOacieeahOWHveaVsOaIluiHque8luWHveaVsOadpeaLn+WQiOabsue6v+OAguS+i+Wmgiwg5Zyo5a+55YmC6YePLeaViOW6lOabsue6v+e7mOWbvueahOWunuaImOS4rSwg6YeH55SoZHJj5YyF5Lit55qEbG9nLWxvZ2lzdGljc+Wbm+WPguaVsOaWueeoi+adpeaLn+WQiOWJgumHjy3mlYjlupTmm7Lnur/jgIINCg0K5Zyo6L+Z5YGP5LiN6ZW/55qE5Y2a5paH5LitLCDlvojpmr7lr7lnZ3Bsb3Qy5Lit5omA5pyJ5Yeg5L2V5a+56LGh5ZKM57uf6K6h5Y+Y5o2i5LiA5LiA6K+m5bC9LCDmm7Tor6bnu4bnmoTlhoXlrrnlj6/ku6XlnKhbZ2dwbG90MueahOWumOaWuee0ouW8lV0oaHR0cDovL2RvY3MuZ2dwbG90Mi5vcmcvY3VycmVudC8p5om+5Yiw44CC5L2G5piv6YCa6L+H5YmN5LiA5bCP6IqC5pyJ5YWz5LqO5Zu+5bGC55qE6K6y6KejLCDmiJHku6zlt7Log73lvojlrrnmmJPnmoTpgJrov4fov5nkupvnu4Tku7bmlbTlkIjlh7rkuIDkupvlpI3mnYLnmoTlm77lvaIsIOiAjOi/meS6m+e7hOS7tueahOWOn+eQhuS5n+aYr+ebuOWQjOeahOOAgg0KDQoNCiMjIyA0LiDnu5/orqHlj5jmjaIoc3RhdHMpDQoNCuW9k+aIkeS7rOmcgOimgeWxleekuuWHuuafkOS4quWPmOmHj+eahOafkOenjee7n+iuoeeJueW+geeahOaXtuWAme+8jOmcgOimgeeUqOWIsOe7n+iuoeWPmOaNouOAguavlOWmguaxguWdh+WAvO+8jOaxguaWueW3ruetie+8jOmAmuW4uOS7peafkOenjeaWueW8j+WvueaVsOaNruS/oeaBr+i/m+ihjOaxh+aAuywg5L6L5aaC6YCa6L+Hc3RhdF9zbW9vdGgoKea3u+WKoOWFiea7keabsue6v+OAgg0KDQo+IOavj+S4gOS4quWHoOS9leWvueixoemDveacieS4gOS4qum7mOiupOeahOe7n+iuoeWPmOaNoiwg5bm25LiU5q+P5LiA5Liq57uf6K6h5Y+Y5o2i6YO95pyJ5LiA5Liq6buY6K6k55qE5Yeg5L2V5a+56LGh44CC5q2j5Zug5aaC5q2kLCDov5nkuIDorr7lrprlsIbkvJrkvb/nu5jlm77ov4fnqIvlj5jnmoTngbXmtLvlpJrlj5jjgIINCg0K5b6I5aSa5Lq65Zyo6Kej6YeKZ2dwbG90MueahOaXtuWAmeWWnOasouivtO+8jGdncGxvdDLnu5jlm77mnInkuKTnp43lh73mlbDvvIzkuIDnsbvmmK9nZW9tX++8jOe7mOWbvueUqOeahO+8m+S4gOexu+aYr3N0YXRf77yM57uf6K6h5Y+Y5o2i55So55qE44CC6L+Z5qC36K+05LiN5piv5LiN5a+577yM5Y+q5piv5b6I5LiN5oGw5b2T77yM5b6I5aSa5Lq65bCx5Lya6Zeu5Ye65LiA5Lqb6Zeu6aKY77yM5q+U5aaC77yM57uf6K6h5Y+Y5o2i56uf54S25piv5YGa6L+Q566X55So55qE77yM5Li65LuA5LmI5Y+v5Lul55So5p2l55S75Zu+77yf5Li65LuA5LmIc3RhdF9iaW7lkoxnZW9tX2hpc3RncmFt55S75Ye65p2l55qE5Zu+5piv5LiA5qC377yM56uf54S25LiA5qC377yM5Li65LuA5LmI6KaB6YeN5aSN77yf5LqL5a6e5LiK77yM5Lu75L2V5LiA5LiqZ2dwbG90MuWbvuWxgumDveWMheaLrHN0YXTlkoxnZW9t5L+p6YOo5YiG77yM5oiW6ICF6K+05Lik5Liq5q2l6aqk77yI5YW25a6e6L+Y5YyF5ouscG9zaXRpb27vvInjgIIg6ICMc3RhdF9pZGVudGl0eeWImeihqOekuuS4jeWBmuS7u+S9leeahOe7n+iuoeWPmOaNouOAguaIkeS7rOadpeS4vuS4quS+i+WtkO+8jOi/mOaYr+S4iumdoueahOS7o+egge+8jOS4uuS6huabtOebtOingu+8jOaIkeWcqOatpOS9nOS6huS/ruaUue+8mg0KDQpgYGB7ciBzdGF0fQ0KeCA8LSBjKHJub3JtKDEwMCwxNCw1KSxyZXAoMjAsMjApKSANCnkgPC0gYyhybm9ybSgxMDAsMTQsNSkgKyBybm9ybSgxMDAsMCwxKSxyZXAoMjAsMjApKQ0KZ2dwbG90KGRhdGE9IE5VTEwsIGFlcyh4ID0geCwgeSA9IHkpKSArICAj5byA5aeL57uY5Zu+DQogIGdlb21fcG9pbnQoY29sb3IgPSAiZGFya3JlZCIpDQpgYGANCg0K5oiR5Lus5p+l55yL56CB5rqQ77yM5bCx55+l6YGTZ2VvbV9wb2ludOeahOm7mOiupHN0YXTmmK9pZGVudGl0ee+8jOWNs+S4jeWBmuS7u+S9lee7n+iuoeWPmOaNou+8mg0KDQpgYGB7cn0NCmdlb21fcG9pbnQNCiMgZnVuY3Rpb24gKG1hcHBpbmcgPSBOVUxMLCBkYXRhID0gTlVMTCwgc3RhdCA9ICJpZGVudGl0eSIsIHBvc2l0aW9uID0gImlkZW50aXR5IiwgDQojICAgICBuYS5ybSA9IEZBTFNFLCAuLi4pIA0KIyB7DQojICAgICBHZW9tUG9pbnQkbmV3KG1hcHBpbmcgPSBtYXBwaW5nLCBkYXRhID0gZGF0YSwgc3RhdCA9IHN0YXQsIA0KIyAgICAgICAgIHBvc2l0aW9uID0gcG9zaXRpb24sIG5hLnJtID0gbmEucm0sIC4uLikNCiMgfQ0KIyA8ZW52aXJvbm1lbnQ6IG5hbWVzcGFjZTpnZ3Bsb3QyPg0KYGBgDQoNCuWkp+WutuWPr+S7peWPkeeOsO+8jOaIkeWcqO+8iDIw77yMMjDvvInov5nkuKrngrnnmoTmlbDmja7kuovlrp7kuIrmmK/mnIkyMOS4queahO+8jOS9hueUseS6juayoeWBmue7n+iuoei9rOaNoigyMCwyMCnov5nkuKrngrnooqvnlLvkuoYyMOasoe+8jOWboOatpOaIkeS7rOeQhuiuuuS4iueci+WIsOeahOeCueWFtuWunuaYr+acgOWQjuS4gOasoeeUu+eahOmCo+S4queCueOAguWPr+iDvei/meS4jeWkn+ebtOingu+8jOayoeWFs+ezu++8jOaIkeS7rOiwg+aVtOS4gOS4i+mAj+aYjuW6puWIsDEwJe+8mg0KYGBge3IgYWxwaGF9DQpnZ3Bsb3QoZGF0YT0gTlVMTCwgYWVzKHggPSB4LCB5ID0geSkpICsgICPlvIDlp4vnu5jlm74NCiAgZ2VvbV9wb2ludChjb2xvciA9ICJkYXJrcmVkIixhbHBoYSA9IDAuMSkNCmBgYA0KDQrov5nmoLflupTor6XlsLHlvojmmI7mmL7kuobvvIznlLHkuo4oMjAsMjAp54K56KKr55S75LqGMjDmrKHvvIzmiYDku6XpgI/mmI7luqbkvJrlj6DliqDkuLoyMCoxMCUgPSAyMDAl5a6e6ZmF5Y+q5bGV546wMTAwJeOAguaIkeS7rOeOsOWcqOWwseS9v+eUqOWdkOagh+i9rOaNouadpemHjeaWsOeUu+i/meS4quWbvu+8mg0KDQpgYGB7cn0NCmdncGxvdChkYXRhPSBOVUxMLCBhZXMoeCA9IHgsIHkgPSB5KSkgKyAgI+W8gOWni+e7mOWbvg0KICBnZW9tX3BvaW50KGNvbG9yID0gImRhcmtyZWQiLHN0YXQgPSAic3VtIikNCmBgYA0KDQrlpb3kuobvvIzop6Pph4rkuIDkuIvvvIxzdGF0X3N1beWunumZheeahOaEj+aAneWwseaYr+aMieeFp+afkOS4gOeCueWNoOaJgOacieeCueWHuueOsOmikeeOh+eEtuWQjuaNoueul+aIkOWkp+Wwj+adpeS9nOWbvu+8jOWboOatpO+8jOS7peS4iuS7o+eggeWwseWPr+S7peW+l+WIsOS4i+mdoui/meW8oOWbvu+8jOWboOS4uigyMCwyMCnov5nkuKrngrnlh7rnjrDpopHnjofkuLoyMC8xMjA9MTYuNjY3Je+8mg0KDQrlpb3kuobvvIzmiJHku6zlj6/ku6Xlj5HnjrDkuobvvIzkuIDkuKrljZXnuq/nmoRnZW9tX3BvaW506YeM6Z2i5Lmf5piv5bim5pyJc3RhdF/nmoTvvIzlm6DmraTvvIzlhbblrp5nZW9tX+WSjHN0YXRf5a6e6ZmF5LiK5piv5LiA5Zue5LqL44CC5Y+v6IO95L2g5Lya6Zeu5LqG77yM6YKj54Wn5oiR55qE6K+05rOV77yM5Lul5LiK6L+Z5bmF5Zu+55So55qE5pivZ2VvbV9wb2ludOmHjOeahOS4gOS4quWPguaVsO+8jOiAjOS4jeaYr+WGjeeUqHN0YXRfc3Vt77yM6L+Z5piv5LiA5Zue5LqL5ZCX77yfYmluZ2/vvIHov5nkuKrpl67popjnm7jlvZPlpb3vvIznmoTnoa7vvIzmjInnhafku6XkuIrnmoTmjqjnkIbvvIzlupTor6XlrZjlnKjkuIDnp43ku6VzdGF0X3N1beS9nOS4uuS4u+WHveaVsOeahOaWueazleadpee7mOWItui/meW5heWbvu+8jOaQnuS4jeWlve+8jOmHjOmdoui/mOacieS4quWPguaVsGdlb23vvIzopoHorr7nva7miJDigJxwb2ludOKAneOAguaIkeS7rOadpeWunui3teS4gOS4i+WQp++8mg0KDQpgYGB7ciBzdGF0X3N1bX0NCmdncGxvdChkYXRhPSBOVUxMLCBhZXMoeCA9IHgsIHkgPSB5KSkgKyAgI+W8gOWni+e7mOWbvg0KICBzdGF0X3N1bShjb2xvciA9ICJkYXJrcmVkIixnZW9tID0gInBvaW50IikNCg0KIyBzdGF0aXN0aWNzIOWFtuWunmdlb23pg73ljIXlkKvkuobpu5jorqTnmoRzdGF077yMDQojIOavlOWmgmdlb21fYmFy6buY6K6k55qE5bCx5pivZ2VvbV9iYXIoc3RhdCA9ICJjb3VudCIp44CCDQojIOaJgOS7peS4i+mdouS+i+WtkOmHjOeUqHN0YXRfY291bnQoICnnmoTnu5PmnpzlkoxnZW9tX2JhciggKeS4gOagt+OAgg0KZ2dwbG90KG1wZywgYWVzKGNsYXNzKSkgKw0KICBzdGF0X2NvdW50KCkNCg0KZ2dwbG90KG1wZywgYWVzKGNsYXNzKSkgKw0KICBnZW9tX2JhcihzdGF0ID0gImNvdW50IikNCmBgYA0KDQrov5jnnJ/lj6/ku6XvvIzov5jplb/lvpfkuIDmqKHkuIDmoLfjgIINCg0K546w5Zyo5bCx6K6y6YCa5LqG77yM5a+55LqO5pyJ6L+H57uP6aqM55qE5ZCM5a2m546w5Zyo5bqU6K+l6YeN5paw5L+u5q2j6L+Z5Liq6KeC54K54oCU4oCUc3RhdF/lkoxnZW9tX+aYr+S4pOenjee7mOWbvuaWueazleOAgg0KDQrov5nmmK/plJnnmoTvvIzlhbblrp7lroPku6zmmK9nZ3Bsb3Qy5q+P5LiA5Liq5Zu+5bGC57uY5Yi26YO95b+F6aG75pyJ55qE77yM5piv5LiA5Liq5Zu+5bGC55qE5LiA5L2T5Lik6Z2i44CCDQoNCuWcqOi/meS4gOatpeS5i+S4re+8jOaIkeS7rOS5n+imgeWbnuWIsOaIkeS7rOWcqOesrOS4gOatpeaXtuWHuueOsOeahOmXrumimO+8jGFlc+WIsOW6leaYr+S7gOS5iO+8n+S4uuS7gOS5iOivtOS7u+S9leS4juaVsOaNruWQkemHj+mhuuW6j+ebuOWFs++8jOmcgOimgemAkOS4quaMh+WumueahOWPguaVsOmDveW/hemhu+WGmeWcqGFlc+mHjO+8n+S7gOS5iOaXtuWAmWNvbG9y44CBc2hhcGXjgIFzaXpl44CBZmlsbOWGmeWklumdou+8jOS7gOS5iOaXtuWAmeWGmemHjOmdou+8nw0KDQphZXPlrp7pmYXkuIrlgZrnmoTmmK/lsIZhZXPph4znmoTlkJHph4/nmoTpobrluo/pgJDkuKrlnLDnu5jliLbjgILorazlpoLku6XkuIvku6PnoIHvvIjovazoh6pnZW9tX3BvaW505biu5Yqp5paH5qGj5Lit55qE5a6e5L6L77yJ77yaDQoNCmBgYHtyfQ0KcCA8LSBnZ3Bsb3QobXRjYXJzLCBhZXMod3QsIG1wZykpICM8LS0tLSBjb2RlIDENCiMgY29kZSAxOiBnZ3Bsb3TpppblhYjovb3lhaXkuobov5nkuKptdGNhcnPnmoTpm4blkIjvvIznhLblkI7mjIflrprnu5nkuoZtcGfkvZzkuLrlhbZ45Z2Q5qCH5L2N572u77yMd3TkuLp55Z2Q5qCH5L2N572u44CCDQoNCnAgKyBnZW9tX3BvaW50KGFlcyhjb2xvdXIgPSBxc2VjKSkgIzwtLS0tIGNvZGUgMg0KIyBjb2RlIDI6IOaMh+WumuS6hnFzZWPkvZzkuLrlhbbmn5PoibLnmoTmoIflh4bvvIjliIbnu4TvvInvvIxxc2Vj5Li6bnVtZXJpY+WPmOmHj++8jOWboOatpO+8jOW6lOivpemAieaLqei/nue7reWei+eahOagh+Wwuu+8jOiAjOS4jeaYr+WIhue7hOafk+iJsuOAgueEtuWQjuW8gOWni+e7mOWItu+8jOivu+WPlm10Y2FycyRtcGdbMV3jgIFtdGNhcnMkd3RbMV3vvIznoa7lrprkvY3nva7vvIznhLblkI7kuLrlhbbmn5PmiJBtdGNhcnMkcXNlY1sxXeminOiJsu+8m+WGjee7mOWItuesrOS6jOeCueOAguOAguOAgg0KYGBgDQoNCuWboOatpO+8jGFlc+mHjOeahOe+juWtpueJueW+geWFtuWunuWwseaYr+aMieeFp+WQkemHj+mhuuW6j+aMh+Wumuavj+S4quS9jee9rueahOe+juWtpueJueW+ge+8jOWkp+WutuWPr+S7peavlOi+g3RhcHBseeWHveaVsOeahOWGmeazleOAgg0KDQrlpb3kuobvvIznjrDlnKjpl67popjlsLHmnaXkuobjgILmiJHmg7PkuLrmiYDmnInngrnnmoTpopzoibLpg73mn5PmiJDnu7/oibLvvIzmgI7kuYjlip7vvJ/lhbblrp7lvojnroDljZXvvIzlpoLmnpzkuI3pnIDopoHmjIflrprov5nkuYjkuIDkuKrmn5PoibLnmoTpobrluo/vvIzogIzpgInmi6nlsIbmlbTkuKrlm77lsYLmn5PmiJDkuIDnp43popzoibLvvIzliJnlj6rpnIDopoHlsIZjb2xvcuWGmeWcqGFlc+Wklu+8mg0KDQpgYGB7cn0NCnAgKyBnZW9tX3BvaW50KGNvbG9yID0gImdyZWVuIikgDQpgYGANCg0K5ZOm77yM5oCq5LiN5b6X5YaZ5ZyoYWVz6YeM5p+T5Ye65p2l55qE6aKc6Imy5LiN5piv57u/6Imy77yM5L2G5Li65LuA5LmI5YaZ5Yiw6YeM6Z2i5bCx5LiN5Y+v5Lul5LqG77yM5Li65LqG5YaZ5Yiw6YeM6Z2i77yM54S25Ye65p2l55qE5piv57KJ6Imy77yfDQoNCuWlveS6hu+8jOaIkeS7rOWGjeadpeWIhuaekOS4gOS4i+aKimNvbG9yID0gImdyZWVuIuWGmeWIsOS6hmFlc+mHjO+8jOWIsOW6leWPkeeUn+S6huS7gOS5iOOAgg0KDQpgYGB7cn0NCnAgKyBnZW9tX3BvaW50KGFlcyhjb2xvdXIgPSAiZ3JlZW4iKSkNCmBgYA0KDQoNCummluWFiO+8jOaVsOaNrueahOWIneWni+WMlui3n+S4iumdoumCo+S4quS+i+WtkOaYr+ebuOWQjOeahOOAgueEtuWQju+8jOWboOS4umNvbG9y5pS+5Yiw5LqGYWVz6YeM77yM5LqO5pivZ2dwbG905byA5aeL5pCc57SibXRjYXJz6YeM6Z2i55qE5ZCR6YeP5LqG77yM5Y+R546w5rKh5pyJ5Y+rImdyZWVuIueahO+8jOeEtuWQjuWPiOaJvuS6hmdsb2JhbO+8jOS5n+ayoeacieOAguS6juaYr++8jGdncGxvdOWwseW8gOWni+aKiuWug+iupOS9nOS6huS4gOS4quaWsOeahOWQkemHj+OAgg0KDQrnrYnnrYnvvIzmnInkuKrpl67popjvvIzmiJHopoHmjInnhafov5nkuKrlkJHph4/mnaXliIbliKvmn5PoibLvvIzogIzkuovlrp7kuIrvvIzov5nkuKrlkJHph4/plb/luqbkuLox77yM5oCO5LmI5Yqe77yfZ2dwbG905bCx5YWI5oqK5LuW5bGV5byA5oiQ5LqGYGZhY3RvcihyZXAoImdyZWVuIixucm93KG10Y2FycykpLGxldmVscyA9IHVuaXF1ZSgiZ3JlZW4iKSlg77yMYmluZ2/vvIENCg0K546w5Zyo5byA5aeL5p+T6Imy5LqG44CC5ZWK56ys5LiA5Liq5pWw5o2uYG10Y2FycyRtcGdbMV1g44CBYG10Y2FycyR3dFsxXWDvvIzlhbbpopzoibLlj5jph4/mmK8iZ3JlZW4iLOWboOWtkOawtOW5s+aYrzHvvIzmn5PmiJDpu5jorqTosIPoibLnrKzkuIDnp43vvIzlk6bvvIzlsLHmmK/ov5nkuKrom4vom4vnmoTnsonnuqLoibLvvJvlho3mn5PnrKzkuozkuKrvvIzov5jmmK8iZ3JlZW4i77yM5Zug5a2Q5rC05bmz5Lmf5pivMe+8jOafk+aIkOibi+ibi+eahOeyiee6ouiJsu+8my4uLg0KDQrnu4jkuo7lrozmiJDkuobvvIzlkqbvvJ/mgI7kuYjpg73mmK/om4vom4vnmoTnsonnuqLoibLjgILpgJrov4fkuL7kuobov5nkuKrmn5PoibLnmoTkvovlrZDlpKflrrblupTor6Xpg73lvITmh4LkuobvvIxhZXPliLDlupXlnKjlubLku4DkuYjkuobjgILlhbbku5bnmoTnvo7lrabnibnlvoHlhbblrp7kuZ/mmK/lrozlhajkuIDoh7TnmoTjgILlj6rmmK/pnIDopoHop6Pph4pncm91cD0x55qE5oSP5oCd5bCx5piv6K+05LiN5YGa5YiG57uE5p2l6L+b6KGM57uY5Zu+44CCDQoNCuS7gOS5iO+8n+i/mOaYr+aQnuS4jea4heivpeaUvmFlc+mHjOmdoui/mOaYr+Wklumdou+8n+mCo+WwseiusOedgDoNCg0KPiDmg7Pnu5/kuIDmlbTkuKrlm77lsYLml7blsLHmlL7liLBhZXPlpJbvvIzmg7PliIbmiJDkuI3lkIznu4TosIPmlbTvvIzlubbkuJTlt7Lnu4/mnInkuIDkuKrkuI5444CBeemVv+W6puS4gOiHtOeahOWIhue7hOWPmOmHj+S6hu+8jOmCo+WwseaUvuWIsGFlc+mHjOOAgg0KDQrlnKjov5nkuIDmraXph4zvvIzov5jopoHopoHor7TnmoTlsLHmmK/miJHku6zopoHorrLnmoTmmK9nZ3Bsb3Qy5aSn6Ie05YaF572u5LqG5ZOq5Lqb5Zu+77yaDQoNCjEuIOeCue+8iHBvaW50LCB0ZXh077yJ77ya5b6A5b6A5Y+q5pyJeOOAgXnmjIflrprkvY3nva7vvIzmnIlzaGFwZeS9huayoeaciWZpbGwNCg0KMi4g57q/KGxpbmUsdmxpbmUsYWJsaW5lLGhsaW5lLHN0YXRfZnVuY3Rpb27nrYkp77ya5LiA6Iis5piv5Z+65LqO5Ye95pWw5p2l5aSE55CG5L2N572uDQoNCjMuIOacieWQkeWwhOe6vyhzZWdtZW50Ke+8mueJueW+geaYr+aMh+WumuS9jee9ruaciXhlbmTlkox5ZW5k77yM6KGo56S65bCE57q/5pa55ZCRDQoNCjQuIOmdoih0aWxlLCByZWN0Ke+8mui/meexu+S4gOiIrOaciXhtYXgseG1pbix5bWF4LHltaW7mjIflrprkvY3nva4NCg0KNS4g5qOSKGJveHBsb3QsYmluLGJhcixoaXN0b2dyYW0p77ya5b6A5b6A5piv5LqM57u05oiW5LiA57u05Y+Y6YeP77yM5YW35pyJd2lkdGjlsZ7mgKcNCg0KNi4g5bimKHJpYmJvbixzbW9vdGgpOumAj+aYjuaYr+eJueW+ge+8jOaYr+mAj+aYjueahGZpbGwNCg0KNy4g6KGl77ya5YyF5ouscnVn5Zu+77yM6K+v5beu5qOSKGVycm9yYmFyLGVycm9yYmFyaCkNCg0K54S25ZCO77yM5bCx5piv5oyJ54Wn5L2g55qE6ZyA6KaB5LiA5q2l5q2l5Yqg5Zu+5bGC5LqG77yI5L2/55So4oCcK+KAne+8ieOAgg0KDQoNCg0KDQoNCg0KIyMjIDUuIOagh+W6pihzY2FsZSkNCuWdkOagh+eahOavlOS+i+WAvA0K5qCH5bqm5o6n5Yi2552A5pWw5o2u5Yiw5Zu+5b2i5bGe5oCn55qE5pig5bCELCDmm7Tph43opoHnmoTkuIDngrnmmK/moIfluqblsIbmiJHku6znmoTmlbDmja7ovazljJbkuLrop4bop4nkuIrlj6/ku6XmhJ/nn6XnmoTkuJzopb8sIOWmguWkp+Wwj+OAgeminOiJsuOAgeS9jee9ruWSjOW9oueKtuOAguaJgOS7pemAmui/h+agh+W6puWPr+S7peS/ruaUueWdkOagh+i9tOWSjOWbvuS+i+eahOWPguaVsOOAgg0KDQrmjInooagx5omA56S6LCDmiYDmnInmoIfluqbmnoTlu7rlmagoc2NhbGUgY29uc3RydWN0b3Ip6YO95oul5pyJ5LiA5aWX6YCa55So55qE5ZG95ZCN5pa55qGI44CC5a6D5Lus5Lulc2NhbGVf5byA5aS0LCDmjqXkuIvmnaXmmK/lm77lvaLlsZ7mgKfnmoTlkI3np7Ao5L6L5aaCLCAgY29sb3Jf44CBc2hhcGVf5oiWeF8p5pyA5ZCO5Lul5qCH5bqm55qE5ZCN56ew57uT5bC+KOS+i+WmgmdyYWRpZW5044CBaHVl5oiWbWFudWFsKeOAguS7juihqOS4reWPr+S7peWPkeeOsCwg5qCH5bqm5piv5Yy65YiG56a75pWj5ZKM6L+e57ut5Y+Y6YeP55qELCDlm6DmraTlho3lr7nmoIfluqbov5vooYzosIPmlbTkuIDlrpropoHms6jmhI/ljLrliIbjgIINCg0KZ2dwbG90MuS4reeahOagh+W6puWPr+S7peeyl+eVpeeahOWIhuS4ujTnsbvvvJoNCigxKeS9jee9ruagh+W6pjrnlKjkuo7lsIbov57nu63lnovjgIHnprvmlaPlnovlkozml6XmnJ8t5pe26Ze05Z6L5Y+Y6YeP5pig5bCE5Yiw57uY5Zu+5Yy65Z+fLCDku6Xlj4rmnoTpgKDlr7nlupTnmoTlnZDmoIfovbQ7DQoNCigyKeminOiJsuagh+W6pu+8mueUqOS6juWwhui/nue7reWei+WSjOemu+aVo+Wei+WPmOmHj+aYoOWwhOWIsOminOiJsjsNCg0KKDMp5omL5Yqo56a75pWj5Z6L5qCH5bqm77ya55So5LqO5bCG56a75pWj5Z6L5Y+Y6YeP5pig5bCE5Yiw5oiR5Lus6YCJ5oup55qE56ym5Y+35aSn5bCP44CB57q/5p2h57G75Z6L44CB5b2i54q25oiW6aKc6ImyLCDku6Xlj4rliJvlu7rlr7nlupTnmoTlm77kvos7DQrku6Xlj4ooNCnlkIzkuIDlnovmoIfluqbvvJrnlKjkuo7nm7TmjqXlsIblj5jph4/lgLznu5jliLbkuLrlm77lvaLlsZ7mgKcsIOiAjOS4jeWOu+aYoOWwhOS7luS7rOOAgg0KDQrlrp7pmYXlupTnlKjkuK3kv67mlLnmoIfluqbmnIDplb/nlKjnmoTmnIkz5Liq5pa56Z2iDQooMSkg5L+u5pS55Zu+5L6LDQooMikg5L+u5pS55Zu+5b2i5bGe5oCnDQooMykg5L+u5pS55Z2Q5qCH6L20DQoNCuS7i+S6juagh+W6puWGheWuueeahOWkjeadguaApywg5bu66K6u6K+m57uG55qE5Y+C6ICD5aaC5LiL6ZO+5o6l77yaDQooMSnntKLlvJXkuK3mnInlhbNzY2FsZeeahOWGheWuue+8mg0KaHR0cDovL2RvY3MuZ2dwbG90Mi5vcmcvY3VycmVudC9pbmRleC5odG1sDQoNCigyKWNvb2tib29r5Lit5pyJ5YWz5Zu+5L6L55qE5L+u5pS5Og0KaHR0cDovL3d3dy5jb29rYm9vay1yLmNvbS9HcmFwaHMvTGVnZW5kc18oZ2dwbG90MikvDQoNCigzKWNvb2tib29r5Lit5pyJ5YWz5Z2Q5qCH6L2055qE5L+u5pS5Og0KaHR0cDovL3d3dy5jb29rYm9vay1yLmNvbS9HcmFwaHMvQXhlc18oZ2dwbG90MikvDQoNCig0KUNvbG9yQnJld2Vyc+mFjeiJsuaWueahiO+8mg0KaHR0cDovL2NvbG9yYnJld2VyLm9yZw0KDQrlnKjlkI7pnaLnmoTlrp7kvovkuK0sIOavj+S4gOW8oOWujOe+jueahOWbvumDvemcgOimgeWvueWFtuagh+W6pui/m+ihjOe7huiHtOeahOS/ruaUueOAgg0KDQojIyMgNi4g5Z2Q5qCH57O7KGNvb3JkKQ0KDQpjb29yZGluYXRlIOW4uOeUqOeahOaYr2Nvb3JkX2NhcnRlc2lhbu+8jOeUqOazleaYr2Bjb29yZF9jYXJ0ZXNpYW4oeGxpbSA9IGMoeG1pbiwgeG1heCksIHlsaW0gPSBjKHltaW4sIHltYXgpKWDjgIJjb29yZGluYXRl57O757uf5Li76KaB5o6n5Yi25Z2Q5qCH6L2077yM5ZGK6K+JZ2dwbG905Z2Q5qCH6L205LiK5Y+W5YC855qE6IyD5Zu044CC6L+Y5piv5Lul5LiK5Zu+5Li65L6L77yM5oqKeei9tOeahOiMg+WbtOiuvue9ruaIkDDliLA4MO+8mg0KYGBge3IgY29vcmRfY2FydGVzaWFufQ0KZ2dwbG90KG1wZywgYWVzKGNsYXNzKSkgKw0KICBnZW9tX2JhcigpICsNCiAgY29vcmRfY2FydGVzaWFuKHlsaW0gPSBjKDAsIDgwKSkNCmBgYA0KDQoNCg0KIyMjIDcuIOWIhumdoihmYWNldCkNCg0K5YiG6Z2i5ZKM5Zu+5bGC5bCG5Y6f5pWw5o2u5YiH5Ymy56ew5aSa5Liq5bCP5pWw5o2u6ZuG77yM5Y2z5q+P5Liq5Zu+5bGC55qE5q+P5Liq5YiG6Z2i6Z2i5p2/6YO95ZCr5pyJ5LiA5Liq5bCP5pWw5o2u6ZuG44CCDQoNCuS9oOWPr+S7peaKiuWug+aDs+ixoeaIkOS4gOS4quS4iee7tOefqemYte+8muWIhumdoumdouadv+W9ouaIkOS6huS4gOS4qjLnu7TnvZHmoLzvvIzlm77lsYLlnKjnrKzkuInnu7TnmoTmlrnlkJHkuIrlj6DliqDjgIINCg0K5YiG6Z2i6L+Z5Liq5qaC5b+15L2/5b6X5YiG57uE5a+55q+U5Zu+55qE57uY5Yi26Z2e5bi45pa55L6/44CCDQoNCuiAjOWbvuWxgueahOamguW/teS9v+W+l+aIkeS7rOeUqFLnu5jlm77ot5/oibrmnK/lrrbnu5jlm77liKvml6Dkuozoh7TjgIINCg0K5aaCZ2dsb3QoKeWHveaVsOWmguWQjOW8gOWQr+S6huS4gOWdl+eUu+W4g++8jOmAmui/h+KAnCvigJ3lj6DliqDnmoTlh6DkvZXlr7nosaHvvJrnroDnp7BnZW9t77yM5o6n5Yi255Sf5oiQ55qE5Zu+5YOP57G75Z6L77yM5aaCZ2VvbV9saW5lKCksZ2VvbV9wb2ludCgp77yM5Y+v5Lul6L+b6KGM5Zu+5bGC55qE5Y+g5Yqg77yM5Lmf5bCx55u45b2T5LqO5piv5bGC5bGC57uY5Yi277yM5oqK5ZCM5LiA5p2l5rqQ55qE5pWw5o2u6YCa6L+H5LiN5ZCM55qE5Zu+5b2i57G75Z6L5bGV56S65Zyo5ZCM5LiA5bmF5Zu+5Lit77yM5oiW6ICF5oqK5LiN5ZCM5p2l5rqQ55qE5pWw5o2u5b6I5pa55L6/55qE5bGV56S65Zyo5ZCM5LiA5Liq5Zu+5b2i5a+56LGh5Lit44CC5Lmf5Y+v5Lul55CG6Kej5oiQ5q+P5LiA5Z2X55S75biD5piv6YCP5piO55qE77yM6YCa6L+H55S75biD55qE5Y+g5Yqg5p2l5Liw5a+M5oiR5Lus55qE5Zu+5b2i5a+56LGh44CCDQoNCuWIhumdoihmYWNldCnljbPlnKjkuIDkuKrpobXpnaLkuIroh6rliqjmkYbmlL7lpJrluYXlm77lvaIsIOi/meS4gOi/h+eoi+WFiOWwhuaVsOaNruWIkuWIhuS4uuWkmuS4quWtkOmbhiwg54S25ZCO5bCG5q+P5Liq5a2Q6ZuG5L6d5qyh57uY5Yi25Yiw6aG16Z2i55qE5LiN5ZCM6Z2i5p2/5Lit44CCDQoNCmdncGxvdDLmj5DkvpvkuKTnp43liIbpnaLnsbvlnovvvJrnvZHmoLzlnosoZmFjZXRfZ3JpZCnlkozlsIHoo4XlsIHpnaLlnosoZmFjZXRfd3JhcCnjgIINCg0K572R5qC85YiG6Z2i55Sf5oiQ55qE5piv5LiA5LiqMue7tOeahOmdouadv+e9keagvCwg6Z2i5p2/55qE6KGM5LiO5YiX6YCa6L+H5Y+Y6YeP5p2l5a6a5LmJLCDmnKzotKjmmK8y57u055qEOw0KDQrlsIHoo4XliIbpnaLliJnlhYjnlJ/miJDkuIDkuKox57u055qE6Z2i5p2/5p2h5Z2XLCDnhLblkI7lho3liIboo4XliLAy57u05LitLCDmnKzotKjmmK8x57u055qE44CCDQoNCuWcqOW+iOWkmuaDheWGteS4iywg5oiR5Lus5Y+v6IO96ZyA6KaB57uY5Yi25pyJ5Lik5Liqeei9tOeahOWdkOagh+ezuywg6ICM5ZyoZ2dwbG90MuS4rSwg6L+Z56eN5YGa5rOV54m55Yir5LiN5o+Q5YChW3N0YWNrb3ZlcueahOiuqOiuul0oaHR0cDovL3N0YWNrb3ZlcmZsb3cuY29tL3F1ZXN0aW9ucy8zMDk5MjE5L2hvdy10by11c2UtZ2dwbG90Mi1tYWtlLXBsb3Qtd2l0aC0yLXktYXhlcy1vbmUteS1heGlzLW9uLXRoZS1sZWZ0LWFuZC1hbm90aGVyKSwg5Y+v6Kej5Yaz55qE5pa55rOV6KaB5LmI5piv5oqK5Y+Y6YeP5b2S5LiA5YyWLCDopoHkuYjkvr/mmK/ph4fnlKjliIbpnaLmlrnms5XjgIINCg0KYGBge3IgZmFjZXR9DQpwIDwtIGdncGxvdChtdGNhcnMsIGFlcyhtcGcsIHd0KSkgKyBnZW9tX3BvaW50KCkNCg0KI+S7pWN5bOS4uuWIhuexu+WPmOmHjyzmjInliJfmjpINCnAgKyBmYWNldF9ncmlkKC4gfiBjeWwpIA0KDQojd3JhcOS4jmdyaWTnmoTljLrliKsNCnAgKyBmYWNldF93cmFwKCB+IGN5bCwgbnJvdyA9IDMpDQoNCiPku6VjeWzkuLrliIbnsbvlj5jph48NCnAgKyBmYWNldF9ncmlkKGN5bCB+IC4pIA0KDQojd3JhcOS4jmdyaWTnmoTljLrliKsNCnAgKyBmYWNldF93cmFwKCB+IGN5bCwgbmNvbCA9IDMpIA0KIyBwICsgZmFjZXRfd3JhcCggfiBjeWwsIG5yb3cgPSAzKSANCg0KI+ihjOS7pXZz5ZKM5YiX5LulYW3kuLrliIbnsbvlj5jph48NCnAgKyBmYWNldF9ncmlkKHZzIH4gYW0pDQoNCnAgKyBmYWNldF93cmFwKHZzIH4gYW0sIG5jb2wgPSAyKSAjd3JhcOS4jmdyaWQg55qE5Yy65YirDQoNCmBgYA0KDQpmYWNldOWPpuWkluS4gOS4quahiOS+i21hcmdpbnMNCg0KYGBge3IgZmFjZXQgZXhhbXBsZSBtYXJnaW5zfQ0KcCA8LSBwICsgZ2VvbV9zbW9vdGgobWV0aG9kID0gImxtIiwgc2UgPUYsIGFlcyhjb2xvciA9IGZhY3RvcihjeWwpKSkgKyANCiAgZ2VvbV9wb2ludChhZXMoY29sb3IgPSBmYWN0b3IoY3lsKSkpDQpwICsgZmFjZXRfZ3JpZCh2cyB+IGFtKQ0KDQoj5L2/55SobWFyZ2luc+adpeaPj+i/sOi+uemZheWbvg0KcCArIGZhY2V0X2dyaWQodnMgfiBhbSwgbWFyZ2lucyA9IFQpIA0KDQpwICsgZmFjZXRfZ3JpZCggfiBjeWwsIHNjYWxlcyA9ICJmcmVlIikNCnAgKyBmYWNldF9ncmlkKCB+IGN5bCwgc2NhbGVzID0gImZyZWVfeCIpDQpgYGANCg0KZmFjZXTlj6blpJbkuIDkuKrmoYjkvotzY2FsZXMNCg0KYGBge3IgZmFjZXQgZXhhbXBsZSAyfQ0KcCA8LSBnZ3Bsb3QoYWVzKGN0eSwgaHd5KSwgZGF0YSA9IG1wZykgKyBnZW9tX3BvaW50KCkNCg0KI+iwg+aVtHNjYWxlc+eahOagh+W6piwg5YWx5pyJZml4ZWQsIGZyZWUsIGZyZWVfeOWSjGZyZWVfeeWbm+enjeWPmOaNog0KcCArIGZhY2V0X3dyYXAoIH4gY3lsKQ0KDQojIOi/memHjOagh+W6puabtOaUueS4umZyZWUNCnAgKyBmYWNldF93cmFwKCB+IGN5bCwgc2NhbGVzID0gImZyZWUiKSANCg0KI3NwYWNl6K6+572u5Li6ZnJlZeaXtiwg5q+P5YiX55qE5a695bqm5LiO6K+l5YiX55qE5qCH5bqm6IyD5Zu05oiQ5q+U5L6LDQpwICsgZmFjZXRfZ3JpZCguIH4gY3lsLCBzY2FsZXMgPSAiZnJlZSIsIHNwYWNlID0gImZyZWUiKSANCg0KIyPkvb/nlKjoh6rnlLHmoIfluqbmnaXmm7/ku6Mg5Y+M5Z2Q5qCH6L20IOeahOWunuaImOeahOS4gOS4quS+i+WtkA0KYGBgDQoNCmZhY2V05Y+m5aSW5LiA5Liq5qGI5L6LDQpmYWNldOWPr+S7peaKiuS4gOW8oOWbvuaMieWPmOmHj+WIhuWJsuaIkOiLpeW5suWwj+WbvuOAguS4i+mdoueahOS+i+WtkOeUqGZhY2V0X3dyYXDmjInnlJ/kuqfllYbliIbovablnovvvJoNCmBgYHtyfQ0KZmFjZXRfZXhhbXBsZSA8LSBnZ3Bsb3QobXBnLCBhZXMoY2xhc3MpKSArDQogIGdlb21fYmFyKCkgKw0KICBmYWNldF93cmFwKH5tYW51ZmFjdHVyZXIpDQoNCmZhY2V0X2V4YW1wbGUNCmBgYA0KDQoNCiMjIyA4LiDkuLvpopgodGhlbWUp6LCD5pW0DQoNCuS4u+mimOezu+e7n+aOp+WItuedgOWbvuW9ouS4reeahOmdnuaVsOaNruWFg+e0oOWkluingiwg5a6D5LiN5Lya5b2x5ZON5Yeg5L2V5a+56LGh5ZKM5qCH5bqm562J5pWw5o2u5YWD57Sg44CC5Li76aKY5L+u5pS55piv5LiA5Liq5a+557uY5Zu+57K+6ZuV57uG55Ci55qE6L+H56iLLCDkuLvopoHlr7nmoIfpopjjgIHlnZDmoIfovbTmoIfnrb7jgIHlm77kvovmoIfnrb7nrYnmloflrZfosIPmlbQsIOS7peWPiue9keagvOe6v+OAgeiDjOaZr+OAgei9tOmhu+eahOminOiJsuaQremFjeOAgg0KDQpgYGB7ciB0aGVtZSBleGFtcGxlfQ0KcmF0aW5nIDwtIHJ1bmlmKDEwMDAsbWluID0gMSwgbWF4ID0gMTApDQppZCA8LSAxOjEwMDANCm1vdmllcyA8LSBkYXRhLmZyYW1lKGlkLCByYXRpbmcpDQoNCnAgPC0gZ2dwbG90KG1vdmllcywgIGFlcyh4ID0gcmF0aW5nKSkgKyBnZW9tX2hpc3RvZ3JhbShiaW53aWR0aCA9IDEpDQoNCiPnmb3oibLog4zmma8NCnAgKyB0aGVtZV9idygpDQoNCiPpu5jorqTmtYXngbDoibLog4zmma8NCnAgKyB0aGVtZV9ncmV5KCkgDQoNCiNjbGFzc2ljLCB3aGl0ZSBiYWNrZ3JvdW5kDQpjbGFzc2ljVGhlbWUgPC0gZ2dwbG90KG1wZywgYWVzKGNsYXNzKSkgKw0KICAgICAgICAgICAgICBnZW9tX2JhcigpICsNCiAgICAgICAgICAgICAgdGhlbWVfY2xhc3NpYygpDQpjbGFzc2ljVGhlbWUNCmBgYA0KDQrkuLvpopjnlLHmjqfliLblm77lvaLlpJbop4LnmoTlpJrkuKrlhYPntKDnu4TmiJAsIOivpuingVvlrpjmlrnntKLlvJVdKGh0dHA6Ly9kb2NzLmdncGxvdDIub3JnL2N1cnJlbnQvKQ0KDQpgYGB7ciB0aGVtZSBtb2RpZnkgbGFiZWwgYW5kIHRpdGxlfQ0KIyNlbGVtZW50X3RleHQoKeS/ruaUueagh+etvuWSjOagh+mimA0KcCA8LSBwICsgbGFicyh0aXRsZSA9ICJoaXN0b2dyYW0iKQ0KcCArIHRoZW1lKA0KICBwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSAyMCwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgY29sb3IgPSAicmVkIiwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgaGp1c3QgPSAwLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBmYWNlID0gImJvbGQiLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBhbmdsZSA9IDE4MCkpDQpgYGANCg0K5YaF572u5YWD57Sg5YWx5pyJ5Zub5Liq5Z+656GA57G75Z6L77ya5paH5pysKHRleHQpLCDnur/mnaEobGluZSnjgIHnn6nlvaIocmVjdGFuZ2xlKeOAgeepuueZvShibGFuayksIHRleHTkuI7lhbbku5bnsbvlnovmk43kvZznm7jnsbvkvLwsIOWFt+S9k+eahOS+i+WtkOWPr+WPguiAg+e0ouW8lSwg5q2k5aSE55SoZWxlbWVudF9ibGFuaygp5p2l5Y676Zmk54Gw6Imy6IOM5pmv44CCDQpgYGB7ciBubyBiYWNrZ3JvdW5kfQ0KcCArIHRoZW1lKHBhbmVsLmJhY2tncm91bmQgPSBlbGVtZW50X2JsYW5rKCkpICNibGFua+aYr+WOu+aOieafkOenjee7mOWbvuWFg+e0oA0KYGBgDQoNCiMjIyMg5LiA5Liq5Lin5b+D55eF54uC55qE5Li76aKY6K6+572uDQpgYGB7ciB0aGVtZSBzZXR0aW5nfQ0KZ2dwbG90KG1wZywgYWVzKGNsYXNzKSkgKw0KICBnZW9tX2JhcigpICsNCiAgdGhlbWUocGFuZWwuYmFja2dyb3VuZCA9IGVsZW1lbnRfYmxhbmsoKSwNCiAgICAgICAgbGVnZW5kLmtleSA9IGVsZW1lbnRfYmxhbmsoKSwNCiAgICAgICAgbGVnZW5kLmJhY2tncm91bmQgPSBlbGVtZW50X2JsYW5rKCksDQogICAgICAgIHN0cmlwLmJhY2tncm91bmQgPSBlbGVtZW50X2JsYW5rKCksDQogICAgICAgIHBsb3QuYmFja2dyb3VuZCA9IGVsZW1lbnRfcmVjdChmaWxsID0gIiNGRUUwRDIiLCBjb2xvciA9ICJibGFjayIsIHNpemUgPSAzKSwNCiAgICAgICAgcGFuZWwuZ3JpZCA9IGVsZW1lbnRfYmxhbmsoKSwNCiAgICAgICAgYXhpcy5saW5lID0gZWxlbWVudF9saW5lKGNvbG9yID0gImJsYWNrIiksDQogICAgICAgIGF4aXMudGlja3MgPSBlbGVtZW50X2xpbmUoY29sb3IgPSAiYmxhY2siKSwNCiAgICAgICAgc3RyaXAudGV4dCA9IGVsZW1lbnRfdGV4dChzaXplID0gMTYsIGNvbG9yID0gIiM5OTAwMEQiKSwNCiAgICAgICAgYXhpcy50aXRsZS55ID0gZWxlbWVudF90ZXh0KGNvbG9yID0gIiM5OTAwMEQiLCBoanVzdCA9IDAsIGZhY2UgPSAiaXRhbGljIiksDQogICAgICAgIGF4aXMudGl0bGUueCA9IGVsZW1lbnRfdGV4dChjb2xvciA9ICIjOTkwMDBEIiwgaGp1c3QgPSAwLCBmYWNlID0gIml0YWxpYyIpLA0KICAgICAgICBheGlzLnRleHQgPSBlbGVtZW50X3RleHQoY29sb3IgPSAiYmxhY2siKSwNCiAgICAgICAgbGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKQ0KYGBgDQoNCg0KDQoNCiMjIyMg6LCD5pW055qE5a2m6ZeuDQoNCui/memHjOeahOiwg+aVtOS4u+imgeaYr+S9v+eUqOW+ruiwg+WbvuW9oui/meWkp+exu+eahOWHveaVsOWBmue+juWtpueJueW+geOAgeWdkOagh+i9tOOAgeagh+mimOOAgee7mOWbvuS4u+mimOeahOiwg+aVtOOAgui/memDqOWIhuS5n+WwseaYr+e7p+aJv+S6huWRveS7pOW8j+S9nOWbvueahOaAneaDs++8jOS9v2dncGxvdDLnmoTngbXmtLvmgKflop7liqDjgIINCg0K5aaC5L2V5pCc57Si5L2g6KaB55So5LuA5LmI576O5a2m54m55b6B6LCD5pW05Ye95pWw77yM5YW25a6e5bCx5piv5oyJ54Wn576O5a2m54m55b6B55qE5ZCN5a2X5p2l77yM5L6L5aaC77yM5L2g6KaB6LCD5pW055qE5pivZmlsbO+8jOWwseaJvnNjYWxlX2ZpbGxf5LmL5ZCO5bCx5pyJ5LiA5Lqb5LiN5ZCM55qE5p+T6Imy5pa55rOV77yI5YWz5LqO6Imy5b2p77yM5aaC5p6c5pyJ5pe26Ze06L+Y5Lya5re75Yqg55u45YWz55+l6K+G77yJ77yb6LCD5pW055qE5piv5qiq5Z2Q5qCH5qCH5bC677yM5bCx5om+c2NhbGVfeF/nhLblkI7lkI7pnaLot5/kuIrkvaDnmoTmqKrlnZDmoIfnsbvlnovvvJvlhbbku5bpm7flkIzjgIINCg0K5Zyo6LCD5pW05Li76aKY6L+Z5pa56Z2i77yM5YC85b6X6KSS5aWW55qE5piv77yMdGhlbWXlh73mlbDlhbblrp7mnIDlppnnmoTlnLDmlrnmmK/lsIblr7nkuo7mlbDmja7nm7jlhbPnmoTnvo7lrabosIPmlbTlkozkuI7mlbDmja7ml6DlhbPnmoTnvo7lrabosIPmlbTliIbnprvkuobjgIINCg0K6K2s5aaC6K+077yM5oiR5Lus6KaB5pS55Y+YeOi9tOeahOminOiJsu+8jOaIluiAhXBhbmVs55qE5bqV6Imy77yM6L+Z5Liq5YW25a6e5LiO5pWw5o2u5aSE55CG5peg5YWz77yM6L+Z56eN5YiG56a75bCx5Lya5L2/5b6X5oiR5Lus5Y+v5Lul5aaC5q2k5rWB56iL5YyW5Zyw5pON5L2c5L2c5Zu+77yM6ICM5LiN6ZyA6KaB5Zyo6ICD6JmR5pWw5o2u55qE5pe25YCZ6L+Y6KaB5YWz5rOo5Yiw5LiO5pWw5o2u5peg5YWz55qE576O5a2m5Y+C5pWw44CCDQoNCuacieS6uuacieaXtuWAmeS8muinieW+l2dncGxvdDLlvojlpYfmgKrnmoTlnLDmlrnlsLHmmK/kuLrku4DkuYjosIPmlbRsZWdlbmTnmoTml7blgJnvvIzmnInml7bopoHnlKhzY2FsZV/vvIzmnInml7blj4jopoHnlKh0aGVtZe+8jOWFtuWunui/memDveaYr+WvueS6jmdncGxvdDLov5nkuKrorr7orqHnkIblv7XnmoTkuI3nkIbop6PvvIzkvZzogIXnmoTorr7orqHmgJ3ot6/mmK/opoHlsIbmlbDmja7lpITnkIbkuI7mlbDmja7nvo7lrabliIblvIDvvIzmlbDmja7nvo7lrabkuI7mlbDmja7ml6DlhbPnmoTosIPmlbTliIblvIDjgILlhbbmrKHvvIx0aGVtZeWHveaVsOmHh+eUqOS6huWbm+S4queugOWNleWcsOWHveaVsOadpeiwg+aVtOaJgOacieeahOS4u+mimOeJueW+ge+8mg0KDQplbGVtZW50X3RleHTosIPmlbTlrZfkvZPvvIwNCmVsZW1lbnRfbGluZeiwg+aVtOS4u+mimOWGheeahOaJgOaciee6v++8jA0KZWxlbWVudF9yZWN06LCD5pW05omA5pyJ55qE5Z2X77yMDQplbGVtZW50X2JsYW5r5riF56m644CCDQoNCui/meenjeiuvuiuoeebuOW9k+WcsOajkuOAgueUseatpO+8jOS4gOS4quaegeWFt+ivmuaEj+eahOS9nOWbvuW6lOivpemVv+aIkOS4i+mdoui/meS4quagt+WtkO+8mg0KDQpgYGB7ciBiZXR0ZXIgZ2dwbG90IFN0cnVjdHVyZX0NCmdncGxvdChkYXRhID0gLCBhZXMoeCA9ICwgeSA9ICkpICsgDQogICAgZ2VvbV9YWFgoLi4uKSArIC4uLiArIHN0YXRfWFhYKC4uLikgKyAuLi4gKw0KICAgIGFubm90YXRlKC4uLikgKyAuLi4gKw0KICAgIHNjYWxlX1hYWCguLi4pICsgY29vcmRfWFhYKC4uLikgKyBndWlkZXMoLi4uKSArIHRoZW1lKC4uLikNCg0KYGBgDQoNCg0KDQojIyMgOS4g5a2Y5YKo5ZKM6L6T5Ye6Z2dzYXZlDQoNCmdnc2F2ZSgp5pivZ2dwbG90MuenjeeJueacieeahOi+k+WHuuWHveaVsCwg5piv5LiA56eN5p6B5Li65pa55L6/55qE5Ye65Zu+5pa55byPDQoNCmBgYHtyIGdnc2F2ZX0NCnAgPC0gZ2dwbG90KG10Y2FycywgYWVzKHggPSBtcGcsIHkgPSBkaXNwKSkgKyBnZW9tX3BvaW50KCkNCg0KZ2dzYXZlKCBmaWxlID0gIm10Y2Fyc19wbG90LnBuZyIsIA0KICAgICAgICB3aWR0aCA9IDUsIA0KICAgICAgICBoZWlnaHQgPSA2LCANCiAgICAgICAgdHlwZSA9ICJjYWlybyIsIA0KICAgICAgICBkcGkgPSA2MDApIA0KI2NhaXJv5Li65oqX6ZSv6b2/5YyFLCBnZ3Bsb3Tpu5jorqTovpPlh7rljbPkuLpjYWlyb+WkhOeQhg0KYGBgDQoNCmdncGxvdDLmlK/mjIFlcHPnn6Lph4/lm77ovpPlh7osIOWFtuS7luWPr+S7peaUr+aMgeeahOagvOW8j+WMheaLrHBuZywganBnLCBwZGbnrYksIOW5tumAmui/h2dnc2F2ZeWPr+S7peaWueS+v+eahOi/m+ihjOS/ruaUueOAgg0KDQoNCiMjIyMg6auY6LSo6YeP5Zu+54mH6L6T5Ye6DQoNCue7mOWbvuWujOaIkOWQjuacgOWQjuS4gOatpeS+v+aYr+WbvueJh+i+k+WHuu+8jOmrmOi0qOmHj+eahOWbvueJh+i+k+WHuuiuqeS6uui1j+W/g+aCpuebru+8jOiAjOS4jeato+ehrueahOi+k+WHuuaWueW8j+aIluiAheebtOaOpemHh+eUqOaIquWbvueahOaWueW8j+S7juWbvuW9ouiuvuWkh+S4reaIquWPlu+8jOW+l+WIsOeahOWbvueJh+W+gOW+gOaYr+S9juWKo+eahOOAgg0KDQrkuIDluYXpq5jotKjph4/nmoTlm77niYflupTlvZPmjqfliLblm77niYflsLrlr7jlkozlrZfkvZPlpKflsI/vvIzlubblr7nnn6Lph4/lm77ov5vooYzpq5jotKjph4/muLLmn5PvvIzljbPmiYDosJPnmoTmipfplK/pvb/jgIINCg0KUuivreiogOmAmui/h+aUr+aMgUNhaXJv55+i6YeP5Zu+5b2i5aSE55CG55qE57G75bqT77yM5Y+v5Lul5Yib5bu66auY6LSo6YeP55qE55+i6YeP5Zu+5b2iKFBERu+8jFBvc3RTY3JpcHTvvIxTVkcpIOWSjCDkvY3lm74oUE5H77yMSlBFR++8jCBUSUZGKe+8jOWQjOaXtuaUr+aMgeWcqOWQjuWPsOeoi+W6j+S4remrmOi0qOmHj+a4suafk+OAgg0KDQrlnKhnZ3Bsb3Qy5oiR5q+U6L6D5o6o6I2Q55qE5Zu+54mH6L6T5Ye65qC85byP5Li657uP6L+HQ2Fpcm/ljIXlpITnkIbnmoRQREbvvIzlm6DkuLpQREbmoLzlvI/kvZPnp6/lsI/vvIzlkIzml7blj6/ku6XlgqjlrZjkuLrlhbbku5bku7vkvZXmoLzlvI/vvIzpmo/lkI7lho3lsIZQREblgqjlrZjkuLplcHPmoLzlvI/lubblnKhQaG90b3Nob3DkuK3miZPlvIDlgZrmnIDnu4jnmoTosIPmlbTvvIzkvovlpoLosIPmlbTmr5TkvovjgIHoibLlvannqbrpl7TlkoxkcGnvvIjkuIDoiKzmnYLlv5flkozlh7rniYjnpL7opoHmsYJkcGk9MzAw5Lul5LiK77yJ562J44CCDQoNCumineWklumcgOimgeazqOaEj+eahOaYr2dncGxvdDLkuK3nmoTlrZfkvZPlpKflsI/pl67popjvvIzlnKhjb29rYm9vay1y5LiA5Lmm5Lit5oyH5Ye677yM5ZyoZ2dwbG90MuS4ree7neWkp+WkmuaVsOaDheWGteS4i++8jHNpemXnmoTlpKflsI/ku6VtbeiusO+8jOivpue7hueahOiuqOiuuuS5n+WPr+S7peWPguiAg3N0YWNrb3ZlcueahOiuqOiuui4NCg0K6ICM5ZyodGhlbWUoKeS4reWvuWVsZW1lbnRfdGV4dCgp6YeM55qEc2l6Zei/m+ihjOiwg+aVtO+8jOatpOaXtueahHNpemXmmK/ku6Xno4XlgLzvvIhwb2ludHMsIHB0c++8ieadpei/m+ihjOihqOekuuOAgg0KDQrkuIvpnaLku6Uz56eNZ2dwbG90MuenjeW4uOeUqOeahOWbvueJh+i+k+WHuuaWueW8j++8jOi+k+WHuuS4gOW5heS4u+agh+mimOS4ujIwcHRz77yM5qiq57q15Z2Q5qCH5qCH6aKY5Li6MTVwdHPvvIzplb/kuLo4MG1tKDMuMTVpbinvvIzlrr3kuLo2MG1tKDIuMzZpbinnmoTlm77kuLrkvovjgIINCg0KYGBge3Igb3V0cHV0IHByaW50IHF1YWxpdHkgaW1hZ2V9DQpyZXF1aXJlKGdncGxvdDIpDQpyZXF1aXJlKENhaXJvKQ0KDQpnZ3Bsb3QoKSArDQogIGdlb21fdGV4dChhZXMoeCA9IDE2LCB5ID0gMTYpLCBsYWJlbCA9ICJBQkMiLCBzaXplID0gMTEuMjgpICsgI+WwuuWvuOS4ujExLjI4bW3vvIzljbPkuLozMuejhQ0KICBnZW9tX3RleHQoYWVzKHggPSAxNiwgeSA9IDE0LjUpLCBsYWJlbCA9ICJBQkMiLCBzaXplID0gMzIpICsgI+WwuuWvuOS4ujMybW0NCiAgbGFicyggeCA9ICJ4IGF4aXMiLCB5ID0gInkgYXhpcyIpICsNCiAgeWxpbSggYygxNCwgMTYuNSkpICsNCiAgeGxpbSggYygxNS43NSwgMTYuMjUpKSArDQogIHRoZW1lKA0KICAgIGF4aXMudGl0bGUueCA9IGVsZW1lbnRfdGV4dChzaXplID0gMzIpLCPlsLrlr7jkuLozMuejhQ0KICAgIGF4aXMudGl0bGUueSA9IGVsZW1lbnRfdGV4dChzaXplID0gMzIpKSPlsLrlr7jkuLozMuejhQ0KIA0KeCA8LSBzZXEoLTQsNCwgbGVuZ3RoLm91dCA9IDEwMDApDQp5IDwtZG5vcm0oeCkNCmRhdGEgPC0gZGF0YS5mcmFtZSh4LCB5KQ0KIA0KI+eUqENhaXJv5YyF6L6T5Ye6DQpyZXF1aXJlKENhaXJvKQ0KQ2Fpcm9QREYoInBsb3QxLnBkZiIsIDMuMTUsIDMuMTUpICPljZXkvY3kuLroi7Hlr7gNCmdncGxvdChkYXRhLCBhZXMoeCA9IHgsIHkgPSB5KSkgKyBnZW9tX2xpbmUoc2l6ZSA9IDEpICsNCiAgdGhlbWVfYncoKQ0KZGV2Lm9mZigpICPlhbPpl63lm77lg4/orr7lpIfvvIzlkIzml7blgqjlrZjlm77niYcNCiANCnBsb3QyIDwtIGdncGxvdChkYXRhLCBhZXMoeCA9IHgsIHkgPSB5KSkgKyBnZW9tX2xpbmUoc2l6ZSA9IDEpICsNCiAgdGhlbWVfYncoKQ0KcGxvdDINCiPnlKhnZ3NhdmXovpPlh7rvvIzpu5jorqTljbPku6XnlKhDYWlyb+WMhei/m+ihjOaKl+mUr+m9v+WkhOeQhg0KZ2dzYXZlKCJwbG90Mi5wZGYiLCBwbG90Miwgd2lkdGggPSAzLjE1LCBoZWlnaHQgPSAzLjE1KSANCiANCiNSU3R1ZGlv6L6T5Ye6DQpgYGANCg0KIyMjIyDmm7TmlLnlrZfkvZMNCg0K5pu05pS56buY6K6k5a2X5L2T5oiW6ICF6YeH55So5Lit5paH6L6T5Ye65Zu+54mH5piv5Y2B5YiG5oG85Lq655qE5LiA5Lu25LqL5oOF77yM5aW95Zyo5oiR5Lus6L+Y5pyJ5ZCE56eN5ouT5bGV5YyF5ZKM5Yqf6IO95by65aSn55qEUnN0dWRpb+adpeWunueOsOOAgg0KDQrnlKhleHRyYWZvbnTovpPlh7roi7HmloflrZfkvZMNCmV4dHJhZm9udOWMheiDveWkn+ebtOaOpeiwg+eUqOWtl+S9k+aWh+S7tu+8jOWGjemAmui/h0dob3N0c2NyaXB0KOmcgOimgeWuieijhe+8ieWwhuWGmeWFpeeahOWtl+S9k+aPkuWFpeeUn+aIkOeahFBERuS4re+8jOWFt+S9k+S7o+eggeWPr+WPguiAg+S6huS9nOiAheivtOaYjltleHRyYWZvbnRdKGh0dHBzOi8vZ2l0aHViLmNvbS93Y2gvZXh0cmFmb250KQ0KDQrlpb3njqnnmoRzaG93dGV4dA0K6YKx5oCh6L2p5aSn56We5YaZ5LqG5LiA5Liq5aW9546p55qEW3Nob3d0ZXh0XShodHRwOi8vY29zLm5hbWUvMjAxNC8wMS9zaG93dGV4dC1pbnRlcmVzdGluZy1mb250cy1hbmQtZ3JhcGhzLynvvIznoa7lrp7lpb3lpb3njql+DQoNCueugOWNleaYk+eUqOeahFJTdHVkaW/ovpPlh7oNCuacgOeugOWNleWunueUqOeahOi+k+WHuuaWueazlei/mOaYr+S9v+eUqFJTdHVkaW/ovpPlh7rvvIznm7TmjqXosIPnlKjns7vnu5/lrZfkvZMo5oiR55qE5pivd2luN++8jG1hY+WSjGxpbnV45LiL6L+Y5rKh5pyJ6K+V6L+H77yJ5bm26L6T5Ye65Y2z5Y+vDQpgYGB7ciBzaG93dGV4dH0NCiNzaG93dGV4dA0KIyBMb2FkIHBhY2thZ2UNCmlmKCFzdXBwcmVzc1dhcm5pbmdzKHJlcXVpcmUoInNob3d0ZXh0IikpKQ0Kew0KICAgIGluc3RhbGwucGFja2FnZXMoInNob3d0ZXh0IikNCiAgICByZXF1aXJlKCJzaG93dGV4dCIpDQp9DQoNCnJlcXVpcmUoc2hvd3RleHQpDQpyZXF1aXJlKGdncGxvdDIpDQpyZXF1aXJlKENhaXJvKQ0KIyBmb250LmFkZCgiQmxhY2tvYWtTdGQiLCAiQzovL1dpbmRvd3MvL0ZvbnRzLy9CbGFja29ha1N0ZC5vdGYiKQ0KZm9udC5hZGQoIkJSVVNIU0NJIiwgIkM6Ly9XaW5kb3dzLy9Gb250cy8vQlJVU0hTQ0kuVFRGIikNCmZvbnQuYWRkKCJ0aW1lcyIsICJDOi8vV2luZG93cy8vRm9udHMvL3RpbWVzLnR0ZiIpDQpmb250LmFkZCgiU1RIVVBPIiwgIkM6Ly9XaW5kb3dzLy9Gb250cy8vU1RIVVBPLnR0ZiIpDQpDYWlyb1BERigic2hvd3RleHRfb3V0cHV0IiwgOCwgOCkNCnNob3d0ZXh0LmJlZ2luKCkNCmdncGxvdCgpICsNCiAgZ2VvbV90ZXh0KGFlcyh4ID0gMTYsIHkgPSAxNi4yNSksIGxhYmVsID0gIkJsYWNrb2FrIFN0ZCIsIHNpemUgPSA4LCANCiAgICAgICAgICAgIGZhbWlseSA9ICJCbGFja29ha1N0ZCIpICsNCiAgZ2VvbV90ZXh0KGFlcyh4ID0gMTYsIHkgPSAxNiksIGxhYmVsID0iQnJ1c2ggU2NyaXB0IFN0ZCIsIHNpemUgPSAxNiwNCiAgICAgICAgICAgIGZhbWlseSA9ICJCcnVzaFNjcmlwdFN0ZCIpICsNCiAgZ2VvbV90ZXh0KGFlcyh4ID0gMTYsIHkgPSAxNS43NSksIGxhYmVsID0gIlRpbWVzIE5ldyBSb21hbiIsIHNpemUgPSAxNiwNCiAgICAgICAgICAgIGZhbWlseSA9ICJ0aW1lcyIpICsNCiAgZ2VvbV90ZXh0KGFlcyh4ID0gMTYsIHkgPSAxNS41MCksIGxhYmVsID0gIuWNjuaWh+eQpeePgCIsIHNpemUgPSAxNiwNCiAgICAgICAgICAgIGZhbWlseSA9ICJTVEhVUE8iKSArDQogIHlsaW0oYygxNS4yNSwgMTYuNTApKSArDQogIGxhYnMoeCA9ICIiLCB5ID0gIiIpICsNCiAgdGhlbWVfYncoKSAj5Zyo55SoUlN0dWRpb+i+k+WHug0KYGBgDQoNCg0KDQoNCg0KDQoNCiMjIyAxMCBnZ3Bsb3Qy5L2c5Zu+5a6e5oiYDQoNCiMjIyMgMS4g5pe26Ze05bqP5YiX5Zu+DQpnZ3Bsb3RfdGltZXNlcmllc19kYXRhLmNzdg0KDQpgYGB7ciBnZ3Bsb3QgZHJhdyB0aW1lIHNlcmllcyBwbG90fQ0KI+eUqGV4Y2Vs5a+85YWl5pWw5o2uLCDmoLzlvI/kuLpjc3YNCm9yaS5kYXRhIDwtIHJlYWQuY3N2KCJFOi9HaXRodWIveGlhbmd4aW5nOTguZ2l0aHViLmlvL1JfTGVhcm5pbmcvZ2dwbG90X3RpbWVzZXJpZXNfZGF0YS5jc3YiLCBoZWFkZXIgPSBGKQ0KaGVhZChvcmkuZGF0YSkNCiMgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFYxDQojIDEgU3VuIEp1bCAgOCAyMzo1OTowMiBIS1QgMjAxMg0KIyAyICAgICAgICAgICAgICAgICAgICAgICAgIDE5MjINCiMgMyAgICAgICAgICAgICAgICAgICAgICAgIDkxOTM4DQojIDQgTW9uIEp1bCAgOSAyMzo1OTowMSBIS1QgMjAxMg0KIyA1ICAgICAgICAgICAgICAgICAgICAgICAgIDIzNDUNCiMgNiAgICAgICAgICAgICAgICAgICAgICAgMTA4NTIxDQoNCiPku6Xnn6npmLXnmoTmlrnlvI/or7vlhaXmlbDmja4sIOaMieihjOaOkuWIlywg5q+P5LiJ5YiX5o2i5LiA6KGMLOaXtumXtO+8jElQ77yMUFYNCmRhdGEgPC0gbWF0cml4KGFzLm1hdHJpeChvcmkuZGF0YSksIG5yb3cob3JpLmRhdGEpIC8gMywgMywgYnlyb3cgPSBUUlVFKQ0KaGVhZChkYXRhKQ0KIyAgICAgIFssMV0gICAgICAgICAgICAgICAgICAgICAgICAgICBbLDJdICAgWywzXSAgICANCiMgWzEsXSAiU3VuIEp1bCAgOCAyMzo1OTowMiBIS1QgMjAxMiIgIjE5MjIiICI5MTkzOCIgDQojIFsyLF0gIk1vbiBKdWwgIDkgMjM6NTk6MDEgSEtUIDIwMTIiICIyMzQ1IiAiMTA4NTIxIg0KIyBbMyxdICJUdWUgSnVsIDEwIDIzOjU5OjAxIEhLVCAyMDEyIiAiMjI1NSIgIjg5MDM2IiANCiMgWzQsXSAiV2VkIEp1bCAxMSAyMzo1OTowMSBIS1QgMjAxMiIgIjIxNzkiICI4NDE0OSIgDQojIFs1LF0gIlRodSBKdWwgMTIgMjM6NTk6MDIgSEtUIDIwMTIiICIyMjI1IiAiODU1ODMiIA0KIyBbNixdICJGcmkgSnVsIDEzIDIzOjU5OjAxIEhLVCAyMDEyIiAiMjM5MiIgIjc5NTA3IiANCg0KDQojIEdldCBTeXMuZ2V0bG9jYWxlIGZvciByZWNvdmVyeSwgdXNlIFN5cy5zZXRsb2NhbGUoIkxDX1RJTUUiLCBsY3QpDQpsY3QgPC0gU3lzLmdldGxvY2FsZSgiTENfVElNRSIpDQoNCiPlhbPpl63ljLrln5/nibnlrprnmoTml7bpl7TnvJbnoIHmlrnlvI8NClN5cy5zZXRsb2NhbGUoIkxDX1RJTUUiLCAiQyIpDQojeCA8LSBjKCIxamFuMTk2MCIsICIyamFuMTk2MCIsICIzMW1hcjE5NjAiLCAiMzBqdWwxOTYwIikNCiN6IDwtIHN0cnB0aW1lKHgsICIlZCViJVkiKQ0KI3oNCg0KI+eUqGFzLlBPU0lYbHQoKeivu+WFpeWtl+espuS4suaVsOaNruW5tui9rOWMluS4umRhdGXmlbDmja4sIOi1i+WAvOe7mWRhdGUsIOaIlmFzLkRhdGUoKQ0KZGF0ZSA8LSBhcy5QT1NJWGx0KGRhdGFbLCAxXSwgdHogPSAiIiwgIiVhICViICVkICVIOiVNOiVTIEhLVCAlWSIpDQojZGF0ZSA8LSBzdHJwdGltZShkYXRhWywgMV0sICIlYSAlYiAlZCAlSDolTTolUyBIS1QgJVkiKQ0KI3N0cnB0aW1lKCJGcmkgSnVsIDEzIDIzOjU5OjAxIEhLVCAyMDEyIiwgIiVhICViICVkICVIOiVNOiVTIEhLVCAlWSIpDQojIEZyaSBKdWwgMTMgMjM6NTk6MDEgSEtUIDIwMTINCiNzdHJwdGltZSgiVHVlLCAyMyBNYXIgMjAxMCAxNDozNjozOCAtMDQwMCIsICAiJWEsICVkICViICVZICVIOiVNOiVTICV6IikNCg0KI2NoZWNrIGRhdGENCmhlYWQoZGF0YSkNCg0KI+WvuWlw5ZKMcHbmiYDlnKjnmoTliJfovazljJbkuLrmlbDlgLzlnosNCklQIDwtIGFzLm51bWVyaWMoZGF0YVssIDJdKQ0KUFYgPC0gYXMubnVtZXJpYyhkYXRhWywgM10pDQoNCiPmgaLlpI3ljLrln5/nibnlnLDnmoTml7bpl7TnvJbnoIHmlrnlvI8NClN5cy5zZXRsb2NhbGUoIkxDX1RJTUUiLCBsY3QpDQoNCiPnlKhnZ3Bsb3Qy57uY5Zu+DQpyZXF1aXJlKGdncGxvdDIpDQoj55SocmVzaGFwZeWMheS4reeahG1lbHTlh73mlbDliIbop6PmlbDmja4NCnJlcXVpcmUocmVzaGFwZTIpDQoNCnAuZGF0YSA8LSBkYXRhLmZyYW1lKGRhdGUsIElQLCBQVikNCiMg6J6N5ZCI5pWw5o2u77yM5Y+q55WZ5LiL5pel5pyf5YiX77yM5Y+Y6YeP5YiG57uE5YiX5Lul5Y+K5a+55bqU55qE5pWw5YC8DQptZWx0ZGF0YSA8LSBtZWx0KHAuZGF0YSwgaWQgPSAoYygiZGF0ZSIpKSkNCiNjaGVjayBtZWx0ZGF0YQ0KaGVhZChtZWx0ZGF0YSk7dGFpbChtZWx0ZGF0YSkNCiMgICAgICAgICAgICAgICAgICBkYXRlIHZhcmlhYmxlIHZhbHVlDQojIDEgMjAxMi0wNy0wOCAyMzo1OTowMiAgICAgICBJUCAgMTkyMg0KIyAyIDIwMTItMDctMDkgMjM6NTk6MDEgICAgICAgSVAgIDIzNDUNCiMgMyAyMDEyLTA3LTEwIDIzOjU5OjAxICAgICAgIElQICAyMjU1DQojIDQgMjAxMi0wNy0xMSAyMzo1OTowMSAgICAgICBJUCAgMjE3OQ0KIyA1IDIwMTItMDctMTIgMjM6NTk6MDIgICAgICAgSVAgIDIyMjUNCiMgNiAyMDEyLTA3LTEzIDIzOjU5OjAxICAgICAgIElQICAyMzkyDQojICAgICAgICAgICAgICAgICAgICBkYXRlIHZhcmlhYmxlICB2YWx1ZQ0KIyAxNjEgMjAxMi0wOS0yMyAyMzo1OTowMSAgICAgICBQViAxMTg3ODUNCiMgMTYyIDIwMTItMDktMjQgMjM6NTk6MDEgICAgICAgUFYgMTQ0NzY2DQojIDE2MyAyMDEyLTA5LTI1IDIzOjU5OjAxICAgICAgIFBWIDEyMDE0MQ0KIyAxNjQgMjAxMi0wOS0yNiAyMzo1OTowMSAgICAgICBQViAxMTU2MjMNCiMgMTY1IDIwMTItMDktMjcgMjM6NTk6MDEgICAgICAgUFYgMTE4OTY2DQojIDE2NiAyMDEyLTA5LTI4IDIzOjU5OjAyICAgICAgIFBWIDEzMjQ0MA0KDQoj55So5a+5SVDlkoxQVuWBmuWIhumhteWkhOeQhiwgeei9tOWIu+W6puiHqueUseWPmOWMlg0KZ3JhcGhpYyA8LSBnZ3Bsb3QoZGF0YSA9IG1lbHRkYXRhLCANCiAgICAgICAgICAgICAgICAgIGFlcyh4ID0gZGF0ZSwgeSA9IHZhbHVlLCBjb2xvciA9IHZhcmlhYmxlKSkgKyANCiAgICAgICAgICAgIGdlb21fbGluZSgpICsgDQogICAgICAgICAgICBnZW9tX3BvaW50KCkNCiMg5YiG6aG1DQpncmFwaGljIDwtIGdyYXBoaWMgKyBmYWNldF9ncmlkKHZhcmlhYmxlIH4gLiwgc2NhbGVzID0gImZyZWVfeSIpDQojIGdyYXBoaWMNCg0KI+e+juWMliwg5re75Yqg5qCH6aKYLCDlnZDmoIcsIOabtOaUueWbvuS+iw0KZ3JhcGhpYzwtIGdyYXBoaWMgKyBsYWJzKHggPSAi5pel5pyfIiwgeSA9ICLkurrmrKEiLCB0aXRsZSA9ICLmn5DnvZHnq5k35pyI6IezMTDmnIhJUC9QVue7n+iuoSIpICsNCiAgdGhlbWUocGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gMjAsIGZhY2UgPSAiYm9sZCIpKSArDQogIHNjYWxlX2NvbG91cl9kaXNjcmV0ZShuYW1lID0gIiIsbGFiZWxzID0gYygiSVAiLCJQViIpKSArDQogIHRoZW1lKHN0cmlwLnRleHQueSA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDApKQ0KDQojIGNoZWNrIHRoZSBncmFwaGljDQpncmFwaGljDQpgYGANCg0KIyMjIyAyLiDkurrlj6PliIbluIPlm74t57uT5ZCI5Zyw5Zu+LURyYXcgQ2l0eSBQb3B1bGF0aW9uIE1hcA0KDQpgYGB7ciBEcmF3IENpdHkgUG9wdWxhdGlvbiBNYXB9DQpyZXF1aXJlKG1hcHMpDQpyZXF1aXJlKGdncGxvdDIpDQoj55So55u05pa55Zu+55yL5LiLcG9w5pW05L2T55qE5YiG5biDDQoj5Y+v5Lul5Y+R546w5pWw5o2u5YiG5biD6L6D5Y+Y5YyW6L6D5aSnLCDmiYDku6Xlr7lwb3DlgZpsb2fovazljJYNCnFwbG90KHBvcCwgZGF0YSA9IHVzLmNpdGllcywgYmlud2lkdGggPSAwMDAwLCBnZW9tID0gImhpc3RvZ3JhbSIpDQpxcGxvdChsb2cocG9wKSwgZGF0YSA9IHVzLmNpdGllcywgYmlud2lkdGggPSAwLjAzLCBnZW9tID0gImhpc3RvZ3JhbSIpDQogDQoj57uY5Yi26IOM5pmv5Zyw5Zu+DQpVU0EuUE9QIDwtIGdncGxvdCh1cy5jaXRpZXMsIGFlcyh4ID0gbG9uZywgeSA9IGxhdCkpICsgeGxpbSgtMTMwLCAtNjUpICsgYm9yZGVycygic3RhdGUiLCBzaXplPTAuNSkrDQogIGdlb21fcG9pbnQoYWVzKHNpemUgPSBsb2cocG9wKSwgY29sb3IgPSBmYWN0b3IoY2FwaXRhbCksIGFscGhhID0gMS81MCkpKw0KICAj5a+5c2l6Zeagh+W6pueahOiwg+aVtOWPguiAg2h0dHA6Ly9kb2NzLmdncGxvdDIub3JnLzAuOS4zLjEvc2NhbGVfc2l6ZS5odG1sDQogIHNjYWxlX3NpemUocmFuZ2U9YygwLCA3KSwgbmFtZSA9ICJsb2coQ2l0eSBwb3B1bGF0aW9uKSIpKw0KICAj5a+556a75pWj5Z6L6aKc6Imy5Y+Y6YeP55qE5qCH5bqm6LCD5pW05Y+C6ICDaHR0cDovL2RvY3MuZ2dwbG90Mi5vcmcvMC45LjMuMS9zY2FsZV9tYW51YWwuaHRtbA0KICAj5a+56L+e57ut5Z6L6aKc6Imy5qCH6YeP55qE5qCH5bqm6LCD5pW05Y+C6ICDaHR0cDovL2RvY3MuZ2dwbG90Mi5vcmcvMC45LjMuMS9zY2FsZV9icmV3ZXIuaHRtbA0KICAj5ZKMaHR0cDovL2RvY3MuZ2dwbG90Mi5vcmcvMC45LjMuMS9zY2FsZV9ncmFkaWVudDIuaHRtbA0KICBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzID0gYygiYmxhY2siLCAicmVkIiksIGxhYmVscyA9IGMoInN0YXRlIGNhcGl0YWwiLCAiY2l0eSIpKSsNCiAgI+iwg+aVtOWbvuS+iw0KICBndWlkZXMoY29sb3IgPSBndWlkZV9sZWdlbmQodGl0bGU9TlVMTCkpICsgc2NhbGVfYWxwaGEoZ3VpZGUgPSBGQUxTRSkrDQogICPnu5jliLbmoIfpopjlkozlnZDmoIfovbQNCiAgbGFicyh4ID0gImxvbmd0aXR1ZGUiLCB5ID0gImxhdGl0dWRlIiwgdGl0bGUgPSAiQ2l0eSBQb3B1bGF0aW9uIGluIHRoZSBVbml0ZWQgU3RhdGVzIikrDQogIHRoZW1lKHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZT0yMCkpDQoNClVTQS5QT1ANCg0KI+i+k+WHuuWbvuWDjyDlubbnlKhjYWlyb+WMhei/m+ihjOaKl+mUr+m9v+WkhOeQhg0KZ2dzYXZlKFVTQS5QT1AsIGZpbGUgPSAiRTovR2l0aHViL3hpYW5neGluZzk4LmdpdGh1Yi5pby9SX0xlYXJuaW5nL1VTQV9QT1AucG5nIiwgdHlwZSA9ICJjYWlybyIsIHdpZHRoID0gMTAsIGhlaWdodCA9IDYuNzUpDQoNCmBgYA0KDQrlvZPnhLYsIOi/meWPquaYr+eugOWNleeahOWcsOWbvue7mOWItuaWueazlSznu5/orqHkuYvpg73kuIrkuZ/mnInlvojlpJrlpKfniZvmnaXnlKhS57uY5Yi25ZCE56eN5ZCE5qC357K+576O55qE5Zyw5Zu+KDFbTWFwXShodHRwOi8vY29zLm5hbWUvP3M9JUU1JTlDJUIwJUU1JTlCJUJFKSwgMlt2aXN1YWxpemluZy1mbGlnaHRzLWRhdGFdKGh0dHA6Ly9jb3MubmFtZS8yMDE0LzA5L3Zpc3VhbGl6aW5nLWZsaWdodHMtZGF0YS8p44CCDQoNCiMjIyMgMy4g5YmC6YePLeaViOW6lOabsue6v+Wbvi1EcmF3IGRvc2UtZWZmZWN0IHBsb3QNCg0KUuS4reeahGRyY+WMhVtodHRwOi8vd3d3LmJpb2Fzc2F5LmRrL2luZGV4LWZpbGVyL3N0YXJ0L0RyYWZ0RHJjTWFudWFsLnBkZl0oW2h0dHA6Ly93d3cuYmlvYXNzYXkuZGsvaW5kZXgtZmlsZXIvc3RhcnQvRHJhZnREcmNNYW51YWwucGRmKeW+iOWuueaYk+WvueWQhOenjeWJgumHjy3mlYjlupTmm7Lnur/ov5vooYznu5jlm74sIOatpOWkhOmHh+eUqOi+g+S4uuW4uOeUqOeahGxvZy1sb2dpc3RpY+Wbm+WPguaVsOaWueeoi+aLn+WQiOS6huWJgumHjy3mlYjlupTmm7Lnur/jgIINCg0KYGBge3IgZHJhdyBkciBjdXJ2ZX0NCm9yaS5kYXRhIDwtIHJlYWQuY3N2KCJFOi9HaXRodWIveGlhbmd4aW5nOTguZ2l0aHViLmlvL1JfTGVhcm5pbmcvRC1SX2N1cnZlLmNzdiIpDQpyZXF1aXJlKGRyYykNCnJlcXVpcmUocmVzaGFwZTIpDQoj5oqK5pWw5o2u6J6N5ZCIDQptZWx0LmRhdGEgPC0gbWVsdChvcmkuZGF0YSwgaWQgPSBjKCJkb3NlIiksIHZhbHVlLm5hbWUgPSAicmVzcG9uc2UiKVssIC0yXQ0KI+eUqGRyY+WMheS4reeahGxvZy1sb2dpc3RpY+Wbm+WPguaVsOaWueeoi+i/m+ihjOaLn+WQiOW7uuaooQ0KbW9kZWwgPC0gZHJtKHJlc3BvbnNlIH4gZG9zZSwgZGF0YSA9IG1lbHQuZGF0YSwgZmN0ID0gTEwuNChuYW1lcyA9IGMoIlNsb3BlIiwgIkxvd2VyIExpbWl0IiwgIlVwcGVyIExpbWl0IiwgIkVDNTAiKSkpDQoj56Gu5a6aeOi9tOiMg+WbtOW5tuaehOW7uuaVsOaNrumbhg0KbWluIDwtIHJhbmdlKG9yaS5kYXRhJGRvc2UpWzFdDQptYXggPC0gcmFuZ2Uob3JpLmRhdGEkZG9zZSlbMl0NCmxpbmUuZGF0YSA8LSBkYXRhLmZyYW1lKGQucHJlZGljdCA9IHNlcShtaW4sIG1heCwgbGVuZ3RoLm91dCA9IDEwMDApKQ0KI+eUqOaooeWei+mihOa1i+aVsOaNruaehOW7uuaVsOaNrumbhg0KbGluZS5kYXRhJHAucHJlZGljdCA8LSBwcmVkaWN0KG1vZGVsLCBuZXdkYXRhID0gbGluZS5kYXRhKQ0KI+aehOW7uue7mOWbvuaVsOaNriwg6IO95aSf6K6h566X6K+v5beu5qOSDQpyZXF1aXJlKHBseXIpDQpwLmRhdGEgPC0gZGRwbHkobWVsdC5kYXRhLCAuKGRvc2UpLCBjb2x3aXNlKG1lYW4pKQ0KcC5kYXRhJHNkIDwtIGRkcGx5KG1lbHQuZGF0YSwgLihkb3NlKSwgY29sd2lzZShzZCkpWywyXQ0KIA0KcmVxdWlyZShnZ3Bsb3QyKQ0KcCA8LSBnZ3Bsb3QoKSArDQogIGdlb21fZXJyb3JiYXIoZGF0YSA9IHAuZGF0YSwgd2lkdGggPSAwLjEsIHNpemUgPSAxLA0KICAgICAgICAgICAgICAgIGFlcyh5bWF4ID0gcmVzcG9uc2UgKyBzZCwgeW1pbiA9IHJlc3BvbnNlIC0gc2QsIHggPSBkb3NlKSkgKw0KICBnZW9tX3BvaW50KGRhdGEgPSBwLmRhdGEsIGFlcyh4ID0gZG9zZSwgeSA9IHJlc3BvbnNlKSwgDQogICAgICAgICAgICAgY29sb3IgPSAicmVkIiwgYWxwaGEgPSAwLjUsIHNpemUgPSA1KSArDQogIGdlb21fbGluZShkYXRhID0gbGluZS5kYXRhLCBhZXMoeCA9IGQucHJlZGljdCwgeSA9IHAucHJlZGljdCksIA0KICAgICAgICAgICAgICBzaXplID0gMSwgY29sb3IgPSAiYmx1ZSIpICsNCiAgI+aUueWPmOWdkOagh+i9tOmXtOmalA0KICBzY2FsZV94X2xvZzEwKG5hbWUgPSAiRG9zZSIsDQogICAgICAgICAgICAgICAgYnJlYWtzPWMoMC4wNSwgMC4xLCAwLjUsIDEsIDUsIDEwLCA1MCwgMTAwKSkgKw0KICBzY2FsZV95X2NvbnRpbnVvdXMobmFtZSA9ICJSZXNwb25zZSIpICsNCiAgdGhlbWVfYncoKQ0KcA0KI+afpeeci+aLn+WQiOaooeWei+WPguaVsA0Kc3VtbWFyeShtb2RlbCkNCmBgYA0KDQojIyMjIDQuIOS5s+aIv+absue6v+Wbvi1CcmVhc3QgQ3VydmUtLUJheWJlJ3MgZmF2b3JpdGUNCmBgYHtyIGJyZWFzdCBjdXJ2ZX0NCiMj55SoZ2dwbG90MuadpeeUu+WHveaVsA0KbGlicmFyeShnZ3Bsb3QyKQ0KI+ehruWumnjovbTljLrln58NCmYgPC0gZ2dwbG90KGRhdGEuZnJhbWUoeCA9IGMoMC4wMDAwMSwgMSkpLCANCiAgICAgICAgICAgIGFlcyh4LCBjb2xvcj0icGluayIsIHNpemU9MikpDQpicmVhc3RfY3VydmUgPC0gZnVuY3Rpb24oeCkgeygxLzM2KSpleHAoLSgoMzYqeC0zNi8yLjcxODI4MTgyODQ1OTA1KV40KSktMyp4KmxvZzEwKHgpfQ0KZiA8LSBmICsgc3RhdF9mdW5jdGlvbihmdW4gPSBicmVhc3RfY3VydmUpICsgDQogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbj0ibm9uZSIpICsNCiAgI+aXi+i9rOWdkOagh+i9tA0KICBjb29yZF9mbGlwKCkNCmYNCmBgYA0KDQojIyMjICA1LiBNaWNoYWVsaXMtTWVudGVu5Yqo5Yqb5a2m5pa556iLDQoNCltob3dfdG9fdXNlX2dnZ3Bsb3QyX3BhcnQyL10oaHR0cDovL3d3dy5jZWxseXNlLmNvbS9ob3dfdG9fdXNlX2dnZ3Bsb3QyX3BhcnQyLykNCg0K6L+Z5Liq5L6L5a2Q5Lit6YeH55So5Ye66Ieq5paH54yu5Lit55qE5LiA57uE5pyJ5YWz5LqO5rWu6JCN5rCu5pGE5Y+W55qE5pWw5o2u77yM5YWxMuS4pOS4quWPmOmHjzjkuKrop4LmtYvlgLzvvIzlhbbkuK3lupXnianmtZPluqbkuI7mta7okI3nmoTmsK7lj5bpgJ/njofkuYvpl7Tlj6/ku6XpgJrov4dNLU3liqjlipvlrabmlrnnqIvmnaXov5vooYzmj4/ov7DjgILlnKjov5nkuKrkvovlrZDkuK3pppblhYjpgJrov4dubHMoKeagueaNrk0tTeWKqOWKm+WtpuaWueeoi+i/m+ihjOaooeWei+aLn+WQiO+8jOeEtuWQjueUqOmihOa1i+WAvOi/m+ihjOS6hmdncGxvdDLnu5jlm77vvIzkuLvopoHph4fnlKjkuoZS6YeM6Z2i55qE5pWw5a2m6KGo56S65pa55rOVcGxvdG1hdGjlnKjlm77kuK3lsZXnpLrkuoblhazlvI/vvIzlubbpgJrov4dnZ3Bsb3Qy56eN55qEdGhlbWXlr7nlm77lg4/ov5vooYzkuobkv67ppbDjgILpnIDopoHms6jmhI/nmoTlnKhnZW9tX3RleHQoKeW5tuS4jeiDveebtOaOpeS9v+eUqGV4cHJlc3Npb27vvIzpnIDopoHlvIDlkK9wYXJzZSA9IFRVUkXvvIzkuJTnlKjlrZfnrKbkuLLooajnpLrjgIINCg0KYGBge3IgTWljaGFlbGlzLU1lbnRlbiBmdW5jdGlvbn0NCmNvbmMgPC0gYygyLjg1NjgyOSwgNS4wMDUzMDMsIDcuNTE5NDczLCAyMi4xMDE2NjQsIDI3Ljc2OTk3NiwgMzkuMTk4MDI1LCA0NS40ODMyNjksIDIwMy43ODQyMzgpDQpyYXRlIDwtIGMoMTQuNTgzNDIsIDI0Ljc0MTIzLCAzMS4zNDU1MSwgNzIuOTY5ODUsIDc3LjUwMDk5LCA5Ni4wODc5NCwgOTYuOTY2MjQsIDEwOC44ODM3NCkNCkwubWlub3IgPC0gZGF0YS5mcmFtZShjb25jLCByYXRlKQ0KTC5taW5vci5tMSA8LSBubHMocmF0ZSB+IFZtICogY29uYy8oSyArIGNvbmMpLCBkYXRhID0gTC5taW5vciwgI+mHh+eUqE0tTeWKqOWKm+WtpuaWueeoiw0KICAgICAgICAgICAgICAgICAgc3RhcnQgPSBsaXN0KEsgPSAyMCwgVm0gPSAxMjApLCAj5Yid5aeL5YC86K6+572u5Li6Sz0yMO+8jFZtPTEyMA0KICAgICAgICAgICAgICAgICAgdHJhY2UgPSBUUlVFKSAj5Y2g57q/5ouf5ZCI6L+H56iLDQoj56Gu5a6aeOi9tOiMg+WbtOW5tuaehOW7uuaVsOaNrumbhg0KbWluIDwtIHJhbmdlKEwubWlub3IkY29uYylbMV0NCm1heCA8LSByYW5nZShMLm1pbm9yJGNvbmMpWzJdDQpsaW5lLmRhdGEgPC0gZGF0YS5mcmFtZShjb25jID0gc2VxKG1pbiwgbWF4LCBsZW5ndGgub3V0ID0gMTAwMCkpDQoj55So5qih5Z6L6aKE5rWL5pWw5o2u5p6E5bu65pWw5o2u6ZuGDQpsaW5lLmRhdGEkcC5wcmVkaWN0IDwtIHByZWRpY3QoTC5taW5vci5tMSwgbmV3ZGF0YSA9IGxpbmUuZGF0YSkNCiANCnJlcXVpcmUoZ2dwbG90MikNCk1fTWZ1bmN0aW9uIDwtIGdncGxvdCgpICsNCiAgZ2VvbV9wb2ludChhZXMoeCA9IGNvbmMsIHkgPSByYXRlKSwgZGF0YSA9IEwubWlub3IsDQogICAgICAgICAgICAgYWxwaGEgPSAwLjUsIHNpemUgPSA1LCBjb2xvciA9ICJyZWQiKSArDQogIGdlb21fbGluZShhZXMoeCA9IGNvbmMsIHkgPSBwLnByZWRpY3QpLCBkYXRhID0gbGluZS5kYXRhLA0KICAgICAgICAgICAgc2l6ZSA9IDEsIGNvbG9yID0gImJsdWUiKSArDQogIHNjYWxlX3hfY29udGludW91cygNCiAgICBuYW1lID0gZXhwcmVzc2lvbihTdWJzdHJhdGUgfn4gY29uY2VudHJhdGlvbihtbW9sIH5+IG1eMykpLCPph4fnlKhleHByZXNzaW9u5p2l6KGo56S65pWw5a2m5YWs5byPDQogICAgYnJlYWtzID0gc2VxKDAsIDIwMCwgYnkgPSAyNSkpICsNCiAgc2NhbGVfeV9jb250aW51b3VzKA0KICAgIG5hbWUgPSAiVXB0YWtlIHJhdGUgKHdlaWdodC9oKSIsDQogICAgYnJlYWtzID0gc2VxKDAsIDEyMCwgYnkgPSAxMCkpICsNCiAgZ2VvbV90ZXh0KGFlcyh4ID0gMTAwLCB5ID0gNjApLA0KICAgICAgICAgICAgbGFiZWwgPSAiYm9sZGl0YWxpYyhmKGxpc3QoeCwgKGxpc3QoSywgVlttXSkpKSkgPT0gZnJhYyhWW21dJS4leCwgSyt4KSkiLA0KICAgICAgICAgICAgI+azqOaEjyBnZW9tX3RleHTkuK3lpoLmnpznlKhleHByZXNzaW9uKCnmnaXov5vooYzooajovr7vvIzlv4XpobvlvIDlkK9wYXJzZSA9IFRSVUUNCiAgICAgICAgICAgICPlkIzml7bku6XlrZfnrKbkuLIiIueahOW9ouW8j+ihqOekuu+8jOS4jeiDveS9v+eUqGV4cHJlc3Npb24NCiAgICAgICAgICAgIHBhcnNlID0gVFJVRSwgDQogICAgICAgICAgICBzaXplID0gNSwgZmFtaWx5ID0gInRpbWVzIg0KICAgICAgICAgICAgKSArDQogIHRoZW1lX2J3KCkgKw0KICB0aGVtZSgNCiAgICAgICAgYXhpcy50aXRsZS54PWVsZW1lbnRfdGV4dChzaXplPTE2KSwNCiAgICAgICAgYXhpcy50aXRsZS55PWVsZW1lbnRfdGV4dChzaXplPTE2KSwNCiAgICAgICAgYXhpcy50ZXh0Lng9ZWxlbWVudF90ZXh0KHNpemU9MTIpLA0KICAgICAgICBheGlzLnRleHQueT1lbGVtZW50X3RleHQoc2l6ZT0xMikpDQpNX01mdW5jdGlvbg0KYGBgDQoNCiMjIyMgNi4g54Ot5Zu+LWhlYXRtYXANCg0K54Ot5Zu+5piv5LiA56eN5p6B5aW955qE5pWw5o2u5Y+v6KeG5YyW5pa55byP77yM6IO95aSf5riF5qWa55qE5pi+56S65Ye65aSa57u05pWw5o2u5LmL6Ze055qE5YWz6IGU5oCn5ZKM5beu5byC5oCn77yM57OX5LiW55WM5bey57uP5Li65oiR5Lus5bGV546w5LqGUumHjOmdouaJgOW4uOeUqOeahGhlYXRtYXDvvIxnZ3Bsb3Qy5ZKMbGF0dGljZTPnp43ng63lm77nu5jliLbmlrnlvI/vvIzlvZPnhLbpmo/nnYBS55qE5LiN5pat6L+b5q2l77yM5bey57uP5pyJ5aSa56eN5YyF5o+Q5L6b5LqG5pu05Liw5a+M5ZKM5pu0566A5Y2V55qE54Ot5Zu+57uY5Yi25pa55byP77yM5L6L5aaCZ3Bsb3Rz5Lit55qEaGVhdG1hcC4y77yMcGhlYXRtYXDvvIxoZWF0bWFwLnBsdXPnrYnnrYnjgIJnZ3Bsb3Qy6L+b6KGM54Ot5Zu+55qE57uY5Yi25Lmf5Y2B5YiG5pa55L6/77yM54Ot5Zu+55qE5YWz6ZSu5piv6IGa57G777yM5Lik5Liq5Y+v6KGM55qE5pa55qGI5piv5a+56IGa57G757uT5p6c6L+b6KGM5o6S5bqP5ZKM5bCG6IGa57G757uT5p6c5Zug5a2Q5YyW5ZCO5Zu65a6a77yM6YCa6L+H57uT5ZCIcGx5cuWMhe+8jOWPr+S7peW+iOaWueS+v+eahOWunueOsOOAgui/memHjOmHh+eUqOS4gOe7hOadpea6kOS6jldIT+WbveWutuaVsOaNruadpeWvueeDreWbvueahOe7mOWItui/m+ihjO+8jOmmluWFiOaVsOaNruagh+WHhuWMluWSjOato+aAgeWMluWQjuaMiUluZGV455qERO+8iOS4uuWQhOWbveeahOS6uuWPo+aVsOaNru+8iei/m+ihjOaOkuW6j++8jOWGjeWwhuWFtuWboOWtkOWMluWQjuWbuuWumu+8jOeUqGdlb21fdGlsZSgp6L+b6KGM54Ot5Zu+55qE57uY5Yi277yM5ZyoZ2dwbG90MuenjeW3suiDvemAmui/h3NjYWxlX2ZpbGxfZ3JhZGllbnQy5Zyo5LiJ56eN5Z+65pys6Imy6L+b6KGM5riQ5Y+Y44CCDQpgYGB7ciBoZWF0bWFwfQ0KV0hPIDwtIHJlYWQuY3N2KCJFOi9HaXRodWIveGlhbmd4aW5nOTguZ2l0aHViLmlvL1JfTGVhcm5pbmcvV0hPLmNzdiIsIGhlYWRlciA9IFRSVUUpDQpyZXF1aXJlKHBseXIpDQoj5oyJ5oC75Lq65Y+j5pWw5o6S5YiX5pWw5o2uDQpXSE8gPC0gYXJyYW5nZShXSE8sIGRlc2MoRCkpDQoj5bCG5pWw5o2u55qE5ZCN5a2X6L2s5o2i5Li65Zug5a2Q77yM5bm25Zu65a6a5bey5ouN5aW955qEY291bnRyee+8jA0KI+WQjOeQhuWPr+S7peaMieeFp+iBmuexu+eahOe7k+aenOi/m+ihjOaOkuWIlw0KV0hPIDwtIHRyYW5zZm9ybShXSE8sIENvdW50cnkgPSBmYWN0b3IoQ291bnRyeSwgbGV2ZWxzID0gdW5pcXVlKENvdW50cnkpKSkNCiANCnJlcXVpcmUocmVzaGFwZTIpDQpyZXF1aXJlKGdncGxvdDIpDQpyZXF1aXJlKHNjYWxlcykNCnJlcXVpcmUoZ3JpZCkNCg0KI21lbHTmlbDmja4NCm0uV0hPIDwtIG1lbHQoV0hPKQ0KI+agh+WHhuWMlu+8jOavj+aOkuaVsOaNruaYoOWwhOWIsOaMieacgOWwj+WAvOWSjOacgOWkp+WAvOaYoOWwhOWIsCgwLDEp5Yy66Ze0DQptLldITyA8LSBkZHBseShtLldITywgLih2YXJpYWJsZSksIHRyYW5zZm9ybSwgcmVzY2FsZSA9IHJlc2NhbGUodmFsdWUpKQ0KI+agh+WHhuWMluW5tuato+aAgeWMluaVsOaNrg0Kcy5XSE8gPC0gZGRwbHkobS5XSE8sIC4odmFyaWFibGUpLCB0cmFuc2Zvcm0sIHJlc2NhbGUgPSBzY2FsZSh2YWx1ZSkpDQpyZXF1aXJlKGdncGxvdDIpDQpwIDwtIGdncGxvdChzLldITywgYWVzKHZhcmlhYmxlLCBDb3VudHJ5KSkgKw0KICAj55SodGlsZeadpei/m+ihjOe7mOeDreWKm+Wbvg0KICBnZW9tX3RpbGUoYWVzKGZpbGw9cmVzY2FsZSkpICsNCiAgc2NhbGVfZmlsbF9ncmFkaWVudDIobWlkPSJibGFjayIsIGhpZ2g9InJlZCIsIGxvdz0iZ3JlZW4iLCBuYW1lID0gIkludGVuc2l0eSIpICsNCiAgbGFicyh4PSJDb3VudHJ5IiwgeT0iSW5kZXgiLCBmYWNlID0gImJvbGQiKSArDQogIHRoZW1lX2J3KCkgKw0KICB0aGVtZSgNCiAgICBheGlzLnRpdGxlLng9ZWxlbWVudF90ZXh0KHNpemU9MTYpLA0KICAgIGF4aXMudGl0bGUueT1lbGVtZW50X3RleHQoc2l6ZT0xNiksDQogICAgYXhpcy50ZXh0Lng9ZWxlbWVudF90ZXh0KHNpemU9MTIsIGNvbG91cj0iZ3JleTUwIiksDQogICAgYXhpcy50ZXh0Lnk9ZWxlbWVudF90ZXh0KHNpemU9MTIsIGNvbG91cj0iZ3JleTUwIiksDQogICAgbGVnZW5kLnRpdGxlPWVsZW1lbnRfdGV4dChzaXplPTE0KSwNCiAgICBsZWdlbmQudGV4dD1lbGVtZW50X3RleHQoc2l6ZT0xMiksDQogICAgbGVnZW5kLmtleS5zaXplID0gdW5pdCgwLjgsICJjbSIpKSPpnIDopoHovb3lhaVncmlk5YyF5p2l6LCD5pW0bGVnZW5k55qE5aSn5bCPDQoNCnANCmBgYA0KDQojIyMjIDcuIOeBq+WxseWbvnZhY2FubyBtYXANCg0K54Gr5bGx5Zu+5piv5pWj54K55Zu+55qE5LiA56eN77yM6IO95aSf5b+r6YCf55qE6L6o5Yir5Ye65aSn5Z6L5pWw5o2u6ZuG6YeN5aSN5Y+Y6YeP5LmL6Ze055qE5beu5byC77yM5YW35L2T55qE5LuL57uN5Y+v5Lul5Y+C6ICDd2lraeWSjENvbGluIEdpbGxlc3BpZeeahOWNmuWuou+8jOS4i+mdoueahOS7o+eggeWSjOWbvuaYr+S9v+eUqGdncGxvdDLnmoTlrp7njrDmlrnlvI/jgIINCmBgYHtyIHZhY2FubyBtYXB9DQpyZXF1aXJlKGdncGxvdDIpDQojI2NoYW5nZSB0aGVtZSMjDQpvbGRfdGhlbWUgPC0gdGhlbWVfdXBkYXRlKA0KICBheGlzLnRpY2tzPWVsZW1lbnRfbGluZShjb2xvdXI9ImJsYWNrIiksDQogIHBhbmVsLmdyaWQubWFqb3I9ZWxlbWVudF9ibGFuaygpLA0KICBwYW5lbC5ncmlkLm1pbm9yPWVsZW1lbnRfYmxhbmsoKSwNCiAgcGFuZWwuYmFja2dyb3VuZD1lbGVtZW50X2JsYW5rKCksDQogIGF4aXMubGluZT1lbGVtZW50X2xpbmUoc2l6ZT0wLjUpDQopDQoNCiMjSGlnaGxpZ2h0IGdlbmVzIHRoYXQgaGF2ZSBhbiBhYnNvbHV0ZSBmb2xkIGNoYW5nZSA+IDIgYW5kIGEgcC12YWx1ZSA8IEJvbmZlcnJvbmkgY3V0LW9mZg0KYSA8LSByZWFkLnRhYmxlKCJFOi9HaXRodWIveGlhbmd4aW5nOTguZ2l0aHViLmlvL1JfTGVhcm5pbmcvZmx1LnR4dCIsaGVhZGVyPVRSVUUsc2VwPSJcdCIpDQpQLlZhbHVlIDwtIGMoYSRQLlZhbHVlKQ0KRkMgPC0gYyhhJEZDKQ0KZGYgPC0gZGF0YS5mcmFtZShQLlZhbHVlLCBGQykNCmRmLkcgPC0gc3Vic2V0KGRmLCBsb2cyKEZDKSA8IC0xJiBQLlZhbHVlIDwgMC4wNSkgI2RlZmluZSBHcmVlbg0KZGYuRyA8LSBjYmluZChkZi5HLCByZXAoMSwgbnJvdyhkZi5HKSkpDQpjb2xuYW1lcyhkZi5HKVszXSA8LSAiQ29sb3IiDQpkZi5CIDwtIHN1YnNldChkZiwgKGxvZzIoRkMpID49IC0xICYgbG9nMihGQykgPD0gMSkgfCBQLlZhbHVlID49IDAuMDUpICNkZWZpbmUgQmxhY2sNCmRmLkIgPC0gY2JpbmQoZGYuQiwgcmVwKDIsIG5yb3coZGYuQikpKQ0KY29sbmFtZXMoZGYuQilbM10gPC0gIkNvbG9yIg0KZGYuUiA8LSBzdWJzZXQoZGYsIGxvZzIoRkMpID4gMSAmIFAuVmFsdWUgPCAwLjA1KSAjZGVmaW5lIFJlZA0KZGYuUiA8LSBjYmluZChkZi5SLCByZXAoMywgbnJvdyhkZi5SKSkpDQpjb2xuYW1lcyhkZi5SKVszXSA8LSAiQ29sb3IiDQpkZi50IDwtIHJiaW5kKGRmLkcsIGRmLkIsIGRmLlIpDQpkZi50JENvbG9yIDwtIGFzLmZhY3RvcihkZi50JENvbG9yKQ0KIyNDb25zdHJ1Y3QgdGhlIHBsb3Qgb2JqZWN0DQpnZ3Bsb3QoZGF0YSA9IGRmLnQsIGFlcyh4ID0gbG9nMihGQyksIHkgPSAtbG9nMTAoUC5WYWx1ZSksIGNvbG9yPSBDb2xvciApKSArDQogIGdlb21fcG9pbnQoYWxwaGEgPSAwLjUsIHNpemUgPSAxLjc1KSArDQogIHRoZW1lKCBsZWdlbmQucG9zaXRpb24gPSAibm9uZSIpICsNCiAgeGxpbShjKC01LCA1KSkgKyB5bGltKGMoMCwgMjApKSArDQogIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXMgPSBjKCJncmVlbiIsICJibGFjayIsICJyZWQiKSkgKw0KICBsYWJzKHg9ZXhwcmVzc2lvbihsb2dbMl0oRkMpKSwgeT1leHByZXNzaW9uKCAtbG9nWzEwXShQLlZhbHVlKSkpICsNCiAgdGhlbWUoYXhpcy50aXRsZS54PWVsZW1lbnRfdGV4dChzaXplPTIwKSwgDQogICAgICAgIGF4aXMudGV4dC54PWVsZW1lbnRfdGV4dChzaXplPTE1KSkgKw0KICB0aGVtZShheGlzLnRpdGxlLnk9ZWxlbWVudF90ZXh0KHNpemU9MjApLA0KICAgICAgICBheGlzLnRleHQueT1lbGVtZW50X3RleHQoc2l6ZT0xNSkpDQpgYGANCg0KIyMjIyA4LiDmlaPngrnlm75zY2F0dGVyIHBsb3QNCg0KYGBge3Igc2NhdHRlciBwbG90fQ0Kc2NhdHRlciA8LQ0KICBnZ3Bsb3QobXRjYXJzLCBhZXMoDQogICAgeCA9IHd0LA0KICAgIHkgPSBtcGcsDQogICAgY29sb3IgPSBhcy5mYWN0b3IoY3lsKSwNCiAgICBzaGFwZSA9IGFzLmZhY3RvcihjeWwpDQogICkpICsNCiAgZ2VvbV9wb2ludCgpICsNCiAgZ2VvbV9zbW9vdGgobWV0aG9kID0gbG0sIHNlID0gRkFMU0UsIGZ1bGxyYW5nZSA9IFRSVUUpICsNCiAgbGFicyh0aXRsZSA9ICJNaWxlcyBwZXIgZ2FsbG9uIFxuIGFjY29yZGluZyB0byB0aGUgd2VpZ2h0IiwNCiAgeCA9ICJXZWlnaHQgKGxiLzEwMDApIiwgeSA9ICJNaWxlcy8oVVMpIGdhbGxvbiIpICsNCiAgdGhlbWVfY2xhc3NpYygpICsNCiAgc2NhbGVfY29sb3JfYnJld2VyKHBhbGV0dGUgPSAiQWNjZW50IikgKw0KICB0aGVtZV9taW5pbWFsKCkNCnNjYXR0ZXINCmBgYA0KDQpzY2F0dGVyIHBsb3QgMg0KYGBge3Igc2NhdHRlciBwbG90IGV4YW1wbGUyfQ0Kc2NhdHRlcnBsb3QyIDwtIGdncGxvdChtcGcsIGFlcyhjdHksIGh3eSkpICsNCiAgICAgICAgICAgICAgICBnZW9tX3BvaW50KCkgKw0KICAgICAgICAgICAgICAgIGdlb21fc21vb3RoKCkNCg0Kc2NhdHRlcnBsb3QyDQpgYGANCg0KDQoNCiMjIyMgOS4gRGlhbW9uZHMNCmBgYHtyIGRpYW1vbmRzfQ0KcmVxdWlyZSgiZ2dwbG90MiIpDQpwIDwtIGdncGxvdChkYXRhID0gZGlhbW9uZHMsDQogICAgICAgICAgICBhZXMoeCA9IGNhcmF0LCB5ID0gcHJpY2UsIGNvbG91ciA9IGN1dCkpDQoNCiMgYWRkIGxheWVyIGZ1bmN0aW9uIDEgbGF5ZXINCnAxIDwtIHAgKyBsYXllcihnZW9tID0gInBvaW50Iiwgc3RhdCA9ICJpZGVudGl0eSIsIHBvc2l0aW9uID0gImRvZGdlIikNCnAxDQoNCiMgcXVpY2sgYWRkIGxheWVyDQpwICsgZ2VvbV9wb2ludCgpDQpgYGANCg0KDQojIyMjIDEwLiDnm7Tmlrnlm74tQmFyIHBsb3QNCmBgYHtyIGJhciBwbG90IGV4YW1wbGUxfQ0KbGlicmFyeShnZ3Bsb3QyKQ0KbGlicmFyeShncmlkRXh0cmEpDQpoaXN0MSA8LSBnZ3Bsb3QoZGF0YSA9IGRpYW1vbmRzLCBhZXMoeCA9IGNsYXJpdHksIGZpbGwgPSBjdXQpKSsNCiAgZ2VvbV9oaXN0b2dyYW0ocG9zaXRpb24gPSAiZG9kZ2UiLCBzdGF0PSJjb3VudCIpDQpoaXN0MQ0KDQpoaXN0MiA8LSBnZ3Bsb3QoZGF0YSA9IGRpYW1vbmRzLCBhZXMoeCA9IGNsYXJpdHksIGZpbGwgPSBjdXQpKSsNCiAgZ2VvbV9oaXN0b2dyYW0ocG9zaXRpb24gPSAiZmlsbCIsIHN0YXQ9ImNvdW50IikNCmhpc3QyDQoNCmhpc3QzIDwtIGdncGxvdChkYXRhID0gZGlhbW9uZHMsIGFlcyh4ID0gY2xhcml0eSwgZmlsbCA9IGN1dCkpKw0KICBnZW9tX2hpc3RvZ3JhbShwb3NpdGlvbiA9ICJzdGFjayIsIHN0YXQ9ImNvdW50IikNCmhpc3QzDQoNCmdyaWQuYXJyYW5nZShoaXN0MSxoaXN0MixoaXN0MywgbmNvbCA9IDMsIG5yb3cgPSAxKQ0KYGBgDQoNCmJhciBwbG90IGV4YW1wbGUgMg0KDQpgYGB7ciBiYXIgcGxvdCBleGFtcGxlMn0NCmQ8LWdncGxvdChkYXRhID0gZGlhbW9uZHMsIGFlcyh4ID0gY2FyYXQpKQ0KZDE8LWQrDQogIHN0YXRfYmluKGFlcyh5bWF4ID0gLi5jb3VudC4uKSwgYmlud2lkdGggPSAwLjEsZ2VvbSA9ICJhcmVhIikNCmQyPC1kKw0KICBzdGF0X2JpbihhZXMoc2l6ZSA9IC4uZGVuc2l0eS4uKSxiaW53aWR0aCA9IDAuMSwgZ2VvbSA9ICJwb2ludCIscG9zaXRpb24gPSAiaWRlbnRpdHkiKQ0KDQpncmlkLmFycmFuZ2UoZDEsIGQyLCBuY29sID0gMiwgbnJvdyA9IDEpDQpgYGANCg0KYmFyIHBsb3QgZXhhbXBsZSAzDQpgYGB7ciBiYXIgcGxvdCBleGFtcGxlIDN9DQpiYXJwbG90MyA8LSBnZ3Bsb3QobXBnLCBhZXMoY2xhc3MpKSArDQogICAgICAgICAgICBnZW9tX2JhcigpDQoNCmJhcnBsb3QzIA0KDQojIGNvbG9yIGdyb3VwIG1hbnVmYWN0dXJlcg0KYmFycGxvdDQgPC0gZ2dwbG90KG1wZywgYWVzKGNsYXNzLCBmaWxsID0gbWFudWZhY3R1cmVyKSkgKw0KICAgICAgICAgICAgZ2VvbV9iYXIoKQ0KYmFycGxvdDQNCg0KDQpjb2xvciA8LSBnZ3Bsb3QobXBnLCBhZXMoY3R5LCBod3ksIGNvbG9yID0gY2xhc3MpKSArIA0KICAgICAgICAgIGdlb21fcG9pbnQoKQ0KY29sb3INCg0Kc2l6ZSA8LSBnZ3Bsb3QobXBnLCBhZXMoY3R5LCBod3ksIHNpemUgPSBjbGFzcykpICsgDQogICAgICAgICAgZ2VvbV9wb2ludCgpDQpzaXplDQoNCnNoYXBlIDwtIGdncGxvdChtcGcsIGFlcyhjdHksIGh3eSwgc2hhcGUgPSBjbGFzcykpICsNCiAgICAgICAgICBnZW9tX3BvaW50KCkNCnNoYXBlDQoNCmBgYA0KDQojIyMjIDExLiDlsIbkuI3lkIzmnaXmupDnmoTmlbDmja7nlLvlnKjlkIzkuIDlvKDlm77kuIoNCg0K5L6L5aaC55So5qih5Z6L5ouf5ZCI5Ye65p2l55qE6aKE5rWL5YC85omp5YWF5Y6f5aeL5pWw5o2uDQpgYGB7ciBhZGRpdGlvbiBkYXRhfQ0KI+S+i+WtkOadpea6kOS6jnN0YWNrb3ZlcmZsb3cNCmRmMTwtZGF0YS5mcmFtZSh4PTE6MTAseT1ybm9ybSgxMCkpDQpkZjI8LWRhdGEuZnJhbWUoeD0xOjEwLHk9cm5vcm0oMTApKQ0KDQpEcmF3X2FkZF9kYXRhIDwtIGdncGxvdChkZjEsYWVzKHgseSkpICsgDQogICAgICAgICAgICBnZW9tX2xpbmUoYWVzKGNvbG9yPSJGaXJzdCBsaW5lIikpICsgDQogICAgICAgICAgICBnZW9tX2xpbmUoZGF0YT1kZjIsYWVzKGNvbG9yPSJTZWNvbmQgbGluZSIpKSArIA0KICAgICAgICAgICAgbGFicyhjb2xvcj0iTGVnZW5kIHRleHQiKQ0KRHJhd19hZGRfZGF0YQ0KYGBgDQoNCuaIkeW+iOWWnOasoueahOS4gOS4quS+i+WtkOadpeiHqldpY2toYW3vvIzlnKjlkIzkuIDlvKDlm77kuIrnlLvkuobnvo7lm73nmoTljoblj7LlpLHkuJrnjoflkozmgLvnu5/ku7vmnJ/vvJoNCmBgYHtyIHByZXNpZGVudGlhbCB2cyBlY29ub21pY3N9DQpwcmVzaWRlbnRpYWwgPC0gc3Vic2V0KHByZXNpZGVudGlhbCwgc3RhcnQgPiBlY29ub21pY3MkZGF0ZVsxXSkNCmdncGxvdChlY29ub21pY3MpICsgI+esrOS4gOS4quaVsOaNrumbhmVjb25vbWljcw0KICBnZW9tX3JlY3QoICPnlKjnrKzkuozkuKrmlbDmja7pm4ZwcmVzaWRlbnRpYWznlLvplb/mlrnlvaLvvIhyZWN0YW5nbGXvvInvvIznlKjkuI3lkIzpopzoibLooajnpLrkuI3lkIzlhZrmtL4NCiAgYWVzKHhtaW4gPSBzdGFydCwgeG1heCA9IGVuZCwgZmlsbCA9IHBhcnR5KSwgeW1pbiA9IC1JbmYsIHltYXggPSBJbmYsIGFscGhhID0gMC4yLA0KICBkYXRhID0gcHJlc2lkZW50aWFsICPmlbDmja7lkoznrKzkuIDlsYLmiYDnlKjnmoTmlbDmja7kuI3kuIDmoLfvvIzmiYDku6XopoHmmI7noa5kYXRhID0gLi4uDQogICkgKyANCiAgZ2VvbV92bGluZSggI+i/mOaYr+eUqHByZXNpZGVudGlhbOWcqOavj+S9jeaAu+e7n+S4iuS7u+aXtumXtOeUu+erlue6v++8iHZlcnRpY2FsIGxpbmXvvIkNCiAgYWVzKHhpbnRlcmNlcHQgPSBhcy5udW1lcmljKHN0YXJ0KSksIA0KICBkYXRhID0gcHJlc2lkZW50aWFsLA0KICBjb2xvdXIgPSAiZ3JleTUwIiwgYWxwaGEgPSAwLjUNCiAgKSArIA0KICBnZW9tX3RleHQoICPku43nhLbmmK/nlKhwcmVzaWRlbnRpYWzlnKjmr4/mrrXku7vmnJ/lhoV56L20MjUwMOeahOWcsOaWueKAnOeUu+KAneWHuuaAu+e7n+eahOWQjeWtlw0KICBhZXMoeCA9IHN0YXJ0LCB5ID0gMjUwMCwgbGFiZWwgPSBuYW1lKSwNCiAgZGF0YSA9IHByZXNpZGVudGlhbCwNCiAgc2l6ZSA9IDMsIHZqdXN0ID0gMCwgaGp1c3QgPSAwLCBudWRnZV94ID0gNTANCiAgKSArDQogIGdlb21fbGluZShhZXMoZGF0ZSwgdW5lbXBsb3kpKSArICPku6Xml7bpl7TlkozlpLHkuJrkurrmlbDkvZzkuLp46L205ZKMeei9tOeUu+aKmOe6v+Wbvg0KICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXMgPSBjKCJibHVlIiwgInJlZCIpKSAj5omL5Yqo6K6+572u5aGr5YWF6aKc6ImyDQoNCmBgYA0KDQoNCg0KIyMjIyAxMi4gZ2dwbG90MiBwbG90IHNlcXVlbmNlIOWghuenr+acqA0KDQrmjqjojZDkuIDkuKrlpb3nmoRnZ3Bsb3Qy55qE5YGa5Zu+5rWB56iLDQoxLiDmlbDmja7lr7zlhaXvvIzlnZDmoIfovbTmmKDlsITvvIjmjIflrprosIHmqKrnnYDnq5nlnKhY77yM6LCB56uW552A56uZ5ZyoWe+8iQ0KMi4g56uW552A56uZ55qE5pWw5YC85aaC5L2V55So5Zu+5b2i6KGo56S677yM5piv5LiN5piv6L+Y6KaB5YGa5LiA5Lqb5Y+Y5o2iDQozLiDmt7vliqDkuIDkupvms6jph4ror7TmmI7vvIzop6Pph4rkuIDkupvlvILluLjvvJ8NCjQuIOWdkOagh+WIu+W6puOAgei9tOaAjuS5iOiuvue9rg0KNS4gZ3VpZGVz5piv5LuA5LmI77yfDQo2LiDkuLvpopjlvq7osIPnvo7ljJYNCmdncGxvdChkYXRhID0gLCBhZXMoeCA9ICwgeSA9ICkpICsgDQogICAgZ2VvbV9YWFgoLi4uKSArIC4uLiArIHN0YXRfWFhYKC4uLikgKyAuLi4gKw0KICAgIGFubm90YXRlKC4uLikgKyAuLi4gKw0KICAgIHNjYWxlX1hYWCguLi4pICsgY29vcmRfWFhYKC4uLikgKyBndWlkZXMoLi4uKSArIHRoZW1lKC4uLikNCiAgICANCmBgYHtyIGdncGxvdDIgcGxvdCBzZXF1ZW5jZX0NCiMjIOWFiOiuvuWumum7mOiupOeahOaVsOaNruS4jum7mOiupOeahOWbvuW9ouWxnuaAp+aYoOWwhCjlsZ7mgKfkuLrngrnnmoR46L205ZKMeei9tOWdkOagh+S9jee9rikNCnAgPC0gZ2dwbG90KGRzbWFsbCkgKyBhZXMgKGNhcmF0ICwgcHJpY2UpDQoNCiMjIOW8gOWni+Whq+WKoOWHoOS9leWvueixoQ0KcCsgZ2VvbV9wb2ludCgpDQpwKyBnZW9tX3BvaW50KCkgK2dlb21fc21vb3RoKCkNCg0KIyMg5byA5aeL5aGr5Yqg5Yeg5L2V5a+56LGh55qE5bGe5oCnKOminOiJsuadpeihqOekuuS/oeaBrykNCnArIGdlb21fcG9pbnQoYWVzKGNvbG91cj1jb2xvcikgKQ0KcCsgZ2VvbV9wb2ludChhZXMoY29sb3VyPWN1dCkgKQ0KcCsgZ2VvbV90ZXh0KGFlcyhsYWJlbD1jb2xvcikpDQoNCiMjIOaUueWPmOS4gOS4i3novbTooajnpLrnmoTkv6Hmga8NCnArIGdlb21fcG9pbnQgKCBhZXMgKCB5ID0gbG9nICggcHJpY2UpICkgKQ0KDQojIyDliIbnu4TnmoTnur/lm74NCnAgPC0gZ2dwbG90KE94Ym95cywgYWVzKGFnZSxoZWlnaHQsIGdyb3VwPSBTdWJqZWN0KSkgKyBnZW9tX2xpbmUoKQ0KcA0KcCArIGdlb21fc21vb3RoKGFlcyhncm91cD0xKSwgbWV0aG9kID0gImxtIiwgc2l6ZSA9IDIsIHNlPUYpDQoNCmBgYA0KDQojIyMjIDEzLiBnZ3Bsb3QyIGNoYW5nZSBkYXRhIHNldCB0byBkcmF3IHBsb3QNCg0K5L2/55SoZ2dwbG90Mui/mOWPr+S7peabtOeBtea0u+WcsOS9v+eUqOWkmuS4quaVsOaNrumbhu+8jOWmguS4i+mdoueahOS+i+WtkOOAgg0KYGBge3J9DQojIyDmm7TmlLnkuIDkuKrmlbDmja7kvZzlm77vvIzlj6rpnIDopoElKyUNCnAgPC0gZ2dwbG90KGRzbWFsbCwgYWVzKGNhcmF0LCBwcmljZSkpKyBnZW9tX3BvaW50KCkNCnAgJSslIGRpYW1vbmRzDQoNCiMg5Y+v5Lul5pa55L6/5Zyw5Yqg5YWl5qCH6aKY5LiO5YWD5pWw5o2uDQojIyDliqDkuIrmoIfpopgo5Yqg5LiKbGFicykNCnArbGFicyh0aXRsZT0i6L+Z5piv5LiA5Liq5Zu+IikNCg0KIyMg5qCH5rOo5Ye65Lu35qC85pyA6auY55qE6YKj5Liq54K577yI5YaN5Yqg5LiK5LiA5LiqZ2VvbV9wb2ludO+8iQ0KaGlnaGVzdCA9IHN1YnNldCAoZHNtYWxsICwgcHJpY2UgPT0gbWF4IChwcmljZSkpDQpwICsgZ2VvbV9wb2ludChkYXRhPWhpZ2hlc3QsIHNpemU9NiwgY29sb3VyPSAicmVkIixhbHBoYT0wLjUpDQoNCiMjIOS4u+mimOeahOS9v+eUqA0KcCArIHRoZW1lX2dyZXkoKQ0KcCArIHRoZW1lX2J3KCkNCnAgKyB0aGVtZV9jbGFzc2ljKCkNCg0KbGlicmFyeSgiZ2d0aGVtZXMiKQ0KcCArIHRoZW1lX3N0YXRhKCkNCg0KYGBgDQoNCiMjIyMgQWRkIFRpdGxlIGFuZCB4bGFiL3lsYWINCg0KcGxvdCB0aXRsZS94IGxhYi95IGxhYg0KYGBge3IgdGl0bGUteGxhYi15bGFifQ0KdGl0bGVfeGxhYl95bGFiIDwtIGdncGxvdChtcGcsIGFlcyhjdHksIGh3eSkpICsNCiAgZ2VvbV9wb2ludCgpICsNCiAgeGxhYigiY2l0eSBtaWxlcyBwZXIgZ2FsbG9uIikgKyAjeOi9tOWQjeensA0KICB5bGFiKCJoaWdod2F5IG1pbGVzIHBlciBnYWxsb24iKSArICN56L205ZCN56ewDQogIGdndGl0bGUoImNpdHkgdnMuIGhpZ2h3YXkgbWlsZXMgcGVyIGdhbGxvbiIpICPlm77moIfmoIfpopgNCnRpdGxlX3hsYWJfeWxhYg0KYGBgDQoNCg0KDQojIyBEYXkgMSBSZWZlcmVuY2UgYm9vaw0KDQrlhoXlrrnmmK/ln7rkuo7ov5nmnKzkuaZbZ2dwbG90MjogRWxlZ2FudCBHcmFwaGljcyBmb3IgRGF0YSBBbmFseXNpc10oaHR0cDovL3d3dy5hbWF6b24uY29tL2RwLzAzODc5ODE0MDMpDQoNCuWPpuWkluaNruivtOi/meacrOS5n+aYr+S4quS4jemUmeeahOWPguiAg1tSIEdyYXBoaWNzIENvb2tib29rXShodHRwOi8vd3d3LmFtYXpvbi5jb20vZHAvMTQ0OTMxNjk1NikNCg0K5LiL6Z2i5pivZ2dwbG90MueahOS4gOS6m+aWh+aho+WSjGdpdGh1YuS4iueahOa6kOS7o+eggQ0KaHR0cDovL2RvY3MuZ2dwbG90Mi5vcmcvY3VycmVudC8NCmh0dHBzOi8vZ2l0aHViLmNvbS9oYWRsZXkvZ2dwbG90Mg0KDQrmnKznr4fmlofnq6Dmtonlj4pnZ3Bsb3QyOiBFbGVnYW50IEdyYXBoaWNzIGZvciBEYXRhIEFuYWx5c2lzIOS4reeahOesrOS6jOeroA0K5Zyo5q2j5byP5byA5aeL5a2m5LmgZ2dwbG90MuWRveS7pOS5i+WJjSDmiJHku6zpppblhYjorqjorrpxcGxvdA0KcXBsb3TmmK9xdWljayBwbG9055qE57yp5YaZIOaXqOWcqOeUqOacgOeugOefreeahOWRveS7pOeUu+WHuuaIkeS7rOaJgOmcgOeahOWbvg0KDQojIyMgMS4g5pWw5o2uDQoNCummluWFiOWcqFLkuK3lronoo4VnZ3Bsb3QyIGFuZCBJbXBvcnQgRGF0YQ0KYGBge3IgTG9hZCBnZ3Bsb3QyIHBhY2thZ2UgYW5kIGltcG9ydCBkYXRhIH0NCiMgTG9hZCBnZ3Bsb3QyIHBhY2thZ2UNCmlmKCFzdXBwcmVzc1dhcm5pbmdzKHJlcXVpcmUoZ2dwbG90MikpKQ0Kew0KICAgIGluc3RhbGwucGFja2FnZXMoJ2dncGxvdDInKQ0KICAgIHJlcXVpcmUoZ2dwbG90MikNCn0NCg0KIyBSZWFkIGRhdGENClggPC0gcmVhZC5kZWxpbSgiaHR0cDovL3d3dy5zdGF0LnViYy5jYS9+cmlja3cvZ2FwbWluZGVyRGF0YUZpdmVZZWFyLnR4dCIpDQoNCiMgb3V0cHV0IGFzIGNzdiBkYXRhDQp3cml0ZS5jc3YoWCxmaWxlID0gIkU6XFxHaXRodWJcXHhpYW5neGluZzk4LmdpdGh1Yi5pb1xcUl9MZWFybmluZ1xcZ2FwbWluZGVyRGF0YUZpdmVZZWFyLmNzdiIsIHJvdy5uYW1lcyA9IFRSVUUpDQoNCg0KIyBDaGVjayBEYXRhIFggc3RydWN0dXJlDQpzdHIoWCkNCiMgJ2RhdGEuZnJhbWUnOgkxNzA0IG9icy4gb2YgIDYgdmFyaWFibGVzOg0KIyAgJCBjb3VudHJ5ICA6IEZhY3RvciB3LyAxNDIgbGV2ZWxzICJBZmdoYW5pc3RhbiIsLi46IDEgMSAxIDEgMSAxIDEgMSAxIDEgLi4uDQojICAkIHllYXIgICAgIDogaW50ICAxOTUyIDE5NTcgMTk2MiAxOTY3IDE5NzIgMTk3NyAxOTgyIDE5ODcgMTk5MiAxOTk3IC4uLg0KIyAgJCBwb3AgICAgICA6IG51bSAgODQyNTMzMyA5MjQwOTM0IDEwMjY3MDgzIDExNTM3OTY2IDEzMDc5NDYwIC4uLg0KIyAgJCBjb250aW5lbnQ6IEZhY3RvciB3LyA1IGxldmVscyAiQWZyaWNhIiwiQW1lcmljYXMiLC4uOiAzIDMgMyAzIDMgMyAzIDMgMyAzIC4uLg0KIyAgJCBsaWZlRXhwICA6IG51bSAgMjguOCAzMC4zIDMyIDM0IDM2LjEgLi4uDQojICAkIGdkcFBlcmNhcDogbnVtICA3NzkgODIxIDg1MyA4MzYgNzQwIC4uLg0KYGBgDQoNCg0KIyMjIDIuIHFwbG905Z+65pys55So5rOVDQoNCnFwbG905pyA566A5Y2V55qE5L2/55So5pa55rOV5ZKMUiBiYXNl5Lit55qEcGxvdOWfuuacrOS4gOagtyDlj6/ku6XnlKjmnaXnlLvmlaPngrnlm74NCg0KYGBge3IgZHJhdyBnZHBwZXJjYXBpdGFsIHZzIGxpZmVleHBhbmR9DQpxcGxvdChnZHBQZXJjYXAsIGxpZmVFeHAsIGRhdGE9WCkNCmBgYA0KDQrkvb/nlKggY29sb3IgPSB5ZWFyIOWPr+S7peWwhuivpeWPmOmHj+eUqOS4jeWQjOminOiJsuagh+ekuuWHuuadpQ0K6L+Z6YeMeWVhcuaYr+S4qui/nue7reeahOWPmOmHjyDmiYDku6XpopzoibLku6XosLHnmoTlvaLlvI/moIfnpLoNCueUqCBsb2cgPSAieCIg6KGo56S65a+55qiq6L2055qE5Y+Y6YeP6L+b6KGMbG9n5Y+Y5o2iDQpgYGB7ciBzY2F0dGVycGxvdCBjb2xvciBncm91cGJ5IHllYXJ9DQpxcGxvdChnZHBQZXJjYXAsIGxpZmVFeHAsIGRhdGE9WCwgbG9nID0gIngiLCBjb2xvciA9IHllYXIpDQpgYGANCg0KDQrkuZ/lj6/ku6XlsIZ5ZWFy5Y+Y5oiQZmFjdG9yIOatpOaXtuS8mueUqOemu+aVo+eahOminOiJsuagh+ekug0KYGBge3IgY29sb3J9DQpxcGxvdChnZHBQZXJjYXAsIGxpZmVFeHAsIGRhdGE9WCwgbG9nID0gIngiLCBjb2xvciA9IGZhY3Rvcih5ZWFyKSkNCmBgYA0KDQrkvb/nlKggc2l6ZSA9IHBvcCDnlKjmoIflv5fnmoTlpKflsI/mmL7npLror6Xlj5jph4/nmoTlpKflsI8NCmBgYHtyIHNpemV9DQpxcGxvdChnZHBQZXJjYXAsIGxpZmVFeHAsIGRhdGE9WCwgbG9nID0gIngiLCBjb2xvciA9IHllYXIsIHNpemUgPSBwb3ApDQpgYGANCg0KDQrov5jlj6/ku6Xkvb/nlKggc2hhcGUgPSBjb250aW5lbnQg55So5LiN5ZCM55qE5qCH5b+X5p2l5pi+56S66K+l5Y+Y6YePDQpgYGB7ciBzaGFwZX0NCnFwbG90KGdkcFBlcmNhcCwgbGlmZUV4cCwgZGF0YT1YLCBsb2cgPSAieCIsIGNvbG9yID0geWVhciwgc2hhcGUgPSBjb250aW5lbnQpDQpgYGANCg0KDQrmnIDlkI4gYWxwaGE9SSgwLjI1KSDmjIflrprpgI/mmI7luqYgMOS4uuWFqOmAj+aYjiAx5Li65LiN6YCP5piODQrlvZPmlbDmja7ph43lj6DkuKXph43ml7bmr5TovoPmnInnlKgNCmBgYHtyIGFscGhhIHRlc3R9DQpxcGxvdChnZHBQZXJjYXAsIGxpZmVFeHAsIGRhdGE9WCwgbG9nID0gIngiLCBhbHBoYT1JKDAuMjUpKQ0KYGBgDQoNCiMjIyAzLiBnZW9tDQoNCmdlb23mmK9nZW9tZXRyaWMgb2JqZWN055qE566A56ewIOeUqOadpeeUn+aIkOS4jeWQjOenjeexu+eahOWbvg0KDQojIyMjIDMuMSBzbW9vdGgNCg0K6aaW5YWI5pivc21vb3RoIOeUqOadpeaPj+i/sOaVsOaNrueahOW5s+a7kei2i+WKvw0K5rOo5oSP5q2k5aSEIGMoInBvaW50IiwgInNtb290aCIpIOihqOekuuWFiOeUu3BvaW50IOWGjeeUu3Ntb290aA0KYGBge3IgZ2VvbSBwb2ludCBhbmQgc21vb3RofQ0KI3Ntb290aCBsaW5lIGF0IHRvcA0KcXBsb3QoZ2RwUGVyY2FwLCBsaWZlRXhwLCBkYXRhPVgsIGxvZyA9ICJ4IiwgYWxwaGE9SSgwLjUpLCBnZW9tPWMoInBvaW50IiwgInNtb290aCIpKQ0KYGBgDQoNCuebuOWPjeeahCBjKCJzbW9vdGgiLCAicG9pbnQiKSDlsLHmmK/lhYjnlLtzbW9vdGgg5YaN55S7cG9pbnQNCmBgYHtyIGdlb20gc21vdGggYW5kIHBvaW50fQ0KI3BvaW50IGF0IHRvcA0KcXBsb3QoZ2RwUGVyY2FwLCBsaWZlRXhwLCBkYXRhPVgsIGxvZyA9ICJ4IiwgYWxwaGE9SSgwLjUpLCBnZW9tPWMoInNtb290aCIsICJwb2ludCIpKQ0KYGBgDQoNCumZpOS6hum7mOiupOeahOW5s+a7keaWueazleS5i+WkliDov5jlj6/ku6Xoh6rooYzmjIflrpog5q+U5aaC57q/5oCn5qih5Z6LDQpgYGB7ciBsaW5lYXIgbW9kbGV9DQpxcGxvdChnZHBQZXJjYXAsIGxpZmVFeHAsIGRhdGE9WCwgbG9nID0gIngiLCBhbHBoYT1JKDAuNSksIGdlb209YygicG9pbnQiLCAic21vb3RoIiksIG1ldGhvZD1sbSkNCmBgYA0KDQrlj6blpJbov5jlj6/ku6Xoh6rooYzmjIflrprlhazlvI8g5L6L5aaC5aSa6aG55byP5Zue5b2SDQpgYGB7ciBmb3JtdWxhfQ0KcXBsb3QoZ2RwUGVyY2FwLCBsaWZlRXhwLCBkYXRhPVgsIGxvZyA9ICJ4IiwgYWxwaGE9SSgwLjUpLCBnZW9tPWMoInBvaW50IiwgInNtb290aCIpLCBtZXRob2Q9bG0sIGZvcm11bGEgPSB5IH4gcG9seSh4LCAzKSkNCmBgYA0KDQojIyMjIDMuMiBsaW5l5ZKMcGF0aA0KDQpsaW5l5Lya5bCG5pWw5o2u5rK/5qiq6L205pa55ZCR5oyJ6aG65bqP6L+e5o6l6LW35p2lIOS4gOiIrOeUqOadpeihqOekuuaXtumXtOW6j+WIl+aVsOaNrg0KYGBge3IgbGluZX0NCnFwbG90KHBvcCwgbGlmZUV4cCwgZGF0YT1YLCBsb2cgPSAieCIsIGFscGhhPUkoMC41KSwgY29sb3I9eWVhciwgZ2VvbT0ibGluZSIpDQpgYGANCg0KcGF0aOS8muWwhuWOn+Wni+aVsOaNruS4reebuOmCu+eahOS4pOS4queCuei/nuaOpei1t+adpSDkuIDoiKznlKjmnaXooajnpLrkuoznu7TmlbDmja7pmo/ml7bpl7TnmoTlj5jljJYNCmBgYHtyIHBhdGh9DQpxcGxvdChnZHBQZXJjYXAsIGxpZmVFeHAsIGRhdGE9WCwgbG9nID0gIngiLCBhbHBoYT1JKDAuNSksIGNvbG9yPXllYXIsIGdlb209YygicG9pbnQiLCAicGF0aCIpKQ0KYGBgDQoNCiMjIyMgMy4zIGJveHBsb3TlkoxqaXR0ZXINCg0K5ZKMUiBiYXNl5Lit55qEYm94cGxvdOS4gOagtyDmqKrovbTnmoTmlbDmja7pnIDopoHmmK9mYWN0b3INCuazqOaEjyBjb2xvcj1JKCJyZWQiKSDkuK3nmoRJKCnmmK/lv4XpobvnmoQg5ZCm5YiZInJlZCLkvJrooqvlvZPlgZrkuIDkuKrmlrDnmoRmYWN0b3INCg0KYGBge3IgYm94cGxvdH0NClgkeWVhci5mYWMgPC0gZmFjdG9yKFgkeWVhcikNCnFwbG90KHllYXIuZmFjLCBsaWZlRXhwLCBkYXRhPVgsIGNvbG9yPUkoInJlZCIpLCBnZW9tPSJib3hwbG90IikNCmBgYA0KDQpqaXR0ZXLlkoxib3hwbG9057G75Ly8DQpgYGB7ciBqaXR0ZXJ9DQpxcGxvdCh5ZWFyLmZhYywgbGlmZUV4cCwgZGF0YT1YLCBjb2xvcj1JKCJyZWQiKSwgZ2VvbT0iaml0dGVyIikNCmBgYA0KDQojIyMjIDMuNCBoaXN0b2dyYW3lkoxkZW5zaXR5DQoNCui/memHjOS9v+eUqCBmaWxsPWNvbnRpbmVudCDlsIbnm7Tmlrnlm77mjInkuI3lkIznmoRjb250aW5lbnTliIblibLlvIANCmBgYHtyIGhpc3RvZ3JhbX0NCnFwbG90KGxpZmVFeHAsZGF0YT1YLCBnZW9tPSJoaXN0b2dyYW0iLCBmaWxsPWNvbnRpbmVudCkNCmBgYA0KDQpkZW5zaXR55LiOaGlzdG9ncmFt57G75Ly8DQpgYGB7ciBkZW5zaXR5fQ0KcXBsb3QobGlmZUV4cCxkYXRhPVgsIGFscGhhPUkoMC41KSwgZ2VvbT0iZGVuc2l0eSIsIGNvbG9yPWNvbnRpbmVudCkNCmBgYA0KDQojIyMgNC4gZmFjZXRzDQoNCuS9v+eUqGZhY2V0c+WPr+S7peWwhuS4gOS4quWbvuagueaNruS4gOS4quaIluS4pOS4quWPmOmHj+eahOWAvOWIhuWIq+aYvuekuuWHuuadpSDmnInliKnkuo7mm7Tnm7Top4LlnLDov5vooYzmr5TovoMNCn7lt6bovrnooajnpLrmr4/kuIDooYznmoTlj5jph48g5Y+z6L656KGo56S65q+P5LiA5YiX55qE5Y+Y6YePDQrmr5TlpoIgY29udGluZW50fi4g5qC55o2uY29udGluZW505YC855qE5LiN5ZCMIOWwhmRlbnNpdHnnmoTlm77lnKjmr4/kuIDooYzph4zmmL7npLrlh7rmnaUNCmBgYHtyfQ0KcXBsb3QobGlmZUV4cCxkYXRhPVgsIGdlb209ImRlbnNpdHkiLCBmYWNldHM9Y29udGluZW50fi4pDQoNCnFwbG90KGdkcFBlcmNhcCxkYXRhPVgsIGdlb209ImRlbnNpdHkiLCBmYWNldHM9Y29udGluZW50fi4pDQpgYGANCg0KDQoNCuexu+S8vOeahCBmYWNldHM9eWVhcn5jb250aW5lbnQg5qC55o2u5q+P5LiA6KGMeWVhcuWSjOavj+S4gOWIl2NvbnRpbmVudOWAvOeahOS4jeWQjCDlsIZoaXN0b2dyYW3nmoTlm77mmL7npLrlh7rmnaUNCmBgYHtyfQ0KcXBsb3QobGlmZUV4cCxkYXRhPVgsIGdlb209Imhpc3RvZ3JhbSIsIGZhY2V0cz15ZWFyfmNvbnRpbmVudCkNCmBgYA0KDQoNCiMjIFIgR3JhcGhpY3MgQ29va2Jvb2sNCg0KcmdndmlzIHBhY2thZ2UNCmdncGxvdDIgcGFja2FnZQ0KcGxvdGx5IHBhY2thZ2UNCg0KYGBge3J9DQpsaWJyYXJ5KGdjb29rYm9vaykNCnNpbXBsZWRhdA0KYGBgDQo=